How HTTP Works

Glossary Term

Keep-Alive (Persistent Connections)

Keep-alive reuses one TCP connection for many HTTP requests. Learn the HTTP/1.1 default, idle timeout defaults in nginx and Node, and why mismatches cause 502s.

Reviewed 2 min readintermediate3 sourcesMarkdown
On this page

TL;DR: Keep-alive means reusing one TCP connection for multiple HTTP requests. It has been the HTTP/1.1 default since the start; the risk now is a timeout mismatch between two hops, which shows up as random 502s.

Keep-alive, formally a persistent connection, is HTTP/1.1 behavior in which the connection stays open after a response so further requests can reuse it instead of paying for a new TCP and TLS handshake. In HTTP/1.0 you had to opt in with Connection: keep-alive; in HTTP/1.1 you opt out with Connection: close.

On the wire

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 27
Connection: keep-alive
Keep-Alive: timeout=5

In HTTP/1.1 the Connection header here is redundant. The Keep-Alive header is an advisory hint that RFC 9110 and 9112 do not define; support is inconsistent. Persistence also requires the message length to be known, through Content-Length or chunked encoding, so the client knows where one response ends.

Defaults worth knowing

  • nginx: keepalive_timeout 75s for clients. Connections to upstreams are not kept alive unless you configure proxy_http_version 1.1, an empty Connection header and an upstream keepalive pool.
  • Apache: KeepAliveTimeout 5 seconds.
  • Node.js: server.keepAliveTimeout defaults to 5000 ms in current releases (still true in Node 26.10). A change merged upstream raises the default to 65000 ms in a future release, so set it explicitly rather than relying on your version’s default.

Non-obvious facts

  • The 502 race. If a load balancer’s idle timeout (60 seconds by default on an AWS ALB) is longer than the backend’s, the backend can close a connection at the same moment the balancer reuses it. The balancer sees a reset and returns 502. Set the backend’s keep-alive timeout above the balancer’s idle timeout; in Node, raise keepAliveTimeout (and keep headersTimeout above it).
  • Reuse saves round trips. A new HTTPS connection costs a TCP handshake plus a TLS handshake before the first byte of the request.
  • Retries on a dead connection are only safe for idempotent requests. If the connection was closed, a client may resend a GET automatically, but a POST is not safely repeatable; see idempotent.
  • HTTP/1.1 reuse is serial. Each connection handles one request at a time, which is why browsers open several per host. HTTP/2 removes that limit.
  • Idle connections cost memory. Long timeouts on a busy server pin file descriptors, so the default is a trade-off, not a free win.

Go deeper

Frequently asked questions

What is HTTP keep-alive?

It is connection reuse: after a response finishes, the TCP (and TLS) connection stays open so the next request skips the handshakes. It is the default in HTTP/1.1.

How do I turn keep-alive off?

Send Connection: close on the request or response. After that message, the connection is closed.

Does HTTP/2 use the Keep-Alive header?

No. HTTP/2 and HTTP/3 multiplex requests over one long-lived connection, and connection-specific headers such as Keep-Alive and Connection are not allowed there.

Why do I get intermittent 502 errors behind a load balancer?

Often the backend closes idle connections sooner than the load balancer expects, so the balancer sends a request on a connection that was just closed. Make the backend idle timeout longer than the balancer's.

Sources

  1. MDN Web Docs: Connection management in HTTP/1.xdeveloper.mozilla.org
  2. RFC 9112: Persistencerfc-editor.org
  3. Node.js: server.keepAliveTimeoutnodejs.org

Keep going

Browse /search