HTTPIntermediate

Keep-alive and connection reuse

“What does HTTP keep-alive do, what does it cost, and what goes wrong when a client and a server disagree about how long a connection stays open?”

What this tests

  • The concrete savings: handshake RTTs and slow start
  • The state and resource cost of idle connections on the server
  • The idle-timeout race and the errors it produces
  • The difference between HTTP keep-alive and TCP keepalive

Answers by level

Read the beginner answer first and notice what is missing.

Without reuse every request pays a TCP handshake (1 RTT), a TLS handshake (1–2 RTT) and a fresh slow start in which the first response is throttled to ~14 kB per RTT. With persistent connections — the HTTP/1.1 default, Connection: keep-alive in 1.0 — the second request pays none of that: the socket is open, the keys exist, and the congestion window is already grown. On a 100 ms path that is 200–300 ms saved per request; for an API client making thousands of calls it is the difference between a busy pool of 20 connections and a stream of TIME_WAIT sockets and port exhaustion (Keep-Alive and Connection Reuse, Connection Pooling).

The cost is state that outlives the request. Each idle connection holds a file descriptor, a kernel socket with its buffers, and on a TLS server tens of kilobytes of session state; a server holding 50 000 idle keep-alive connections must be sized for it (fd limits, memory) and its event loop or worker model must not tie a thread to an idle socket (The Event-Driven Server). So both ends impose an idle timeout: nginx defaults to 75 s toward clients, Node’s http.Server.keepAliveTimeout to 5 s, AWS ALB to 60 s, Go’s http.Server.IdleTimeout to whatever you set. HTTP/1.1’s Keep-Alive: timeout=5, max=100 header is only a hint.

The race is the classic failure. The server decides an idle connection is dead at exactly the moment the client picks it from the pool and writes a request. The client’s bytes meet a closed socket: it gets ECONNRESET, EPIPE, "socket hang up" (Node) or "Connection reset by peer" for a request that was never processed — or, worse, was fully written and *may* have been processed before the close raced. A proxy between them turns the same event into a 502. The rule that avoids it: the client’s idle timeout must be shorter than the server’s, at every hop. The textbook incident is Node behind an ALB: ALB idle 60 s, Node default 5 s → intermittent 502s until Node is set to keepAliveTimeout = 65 000 (and headersTimeout above it).

This is unrelated to TCP keepalive (SO_KEEPALIVE): kernel probes on an idle connection, by default only after 2 hours (tcp_keepalive_time=7200), whose purpose is to detect a dead peer and to keep NAT and firewall entries from expiring. It does nothing for HTTP request semantics, and its default timing is far too long to matter for the race above.

Green flags · Red flags

Strong green flag · Recognises the Node-behind-ALB 502 pattern (5 s vs 60 s) or an equivalent and knows retry safety depends on whether the request was idempotent.
Green flags
  • Quantifies the saving: TCP + TLS RTTs and slow start
  • Names the resource cost of idle connections and the need for idle timeouts
  • Describes the idle-timeout race and the errors it produces (ECONNRESET, 502)
  • States the rule: client timeout shorter than server timeout at every hop
  • Distinguishes HTTP keep-alive from TCP keepalive
Red flags
  • Thinks keep-alive and TCP keepalive are the same thing
  • Cannot say why a reused connection would ever produce a reset
  • Believes the server should keep connections open forever
  • Suggests retrying every reset request without considering idempotency

Follow-up questions

F1
A service behind nginx gets 502 "upstream prematurely closed connection" about once per thousand requests, only under low traffic. Why low traffic?
F2
You add three backends to a fleet and they stay idle for ten minutes. Why?
F3
Is it safe to retry a POST that failed with ECONNRESET?

Scenario

A Node.js API behind an AWS ALB shows a steady 0.1% of 502s in the ALB logs with no matching errors in the application logs. Explain the mechanism, how you confirm it from the ALB access log fields, and the exact configuration change.

Learn this topic