HTTP Connection Management
HTTP connection management governs how transport connections are established, reused, and closed. Each HTTP version handles connections differently. HTTP/1.0 opened a new TCP connection per request. HTTP/1.1 introduced persistent connections. HTTP/2 multiplexes streams over a single connection. HTTP/3 replaces TCP with QUIC.
TCP foundations
HTTP/1.0 and HTTP/1.1 run on TCP. Every new TCP connection requires a three-way handshake (SYN, SYN-ACK, ACK) before any HTTP data flows. This handshake adds one full round trip of latency to the first request on each connection.
After the handshake, TCP uses a congestion control algorithm starting with a small congestion window and gradually increases throughput. A fresh connection operates at reduced speed until the window opens. Reusing an established connection avoids both the handshake delay and the congestion window ramp-up.
Connection scope
HTTP connections operate hop-by-hop, not end-to-end. A client connects to the first intermediary (proxy, load balancer, CDN edge). The intermediary opens a separate connection to the next hop. Each hop negotiates its own connection parameters independently.
The Connection header controls
hop-by-hop behavior. Options listed in this
header (like close or keep-alive) apply only
to the immediate connection and are not forwarded
to the next hop.
Short-lived connections (HTTP/1.0)
HTTP/1.0 created a new TCP connection for every request-response pair. Each resource on a page (HTML, images, stylesheets, scripts) required a separate connection with its own three-way handshake.
This model consumed significant time in handshakes and never allowed TCP congestion windows to reach efficient throughput.
Persistent connections (HTTP/1.1)
HTTP/1.1 defaults to persistent
connections. Multiple
requests and responses travel over a single TCP
connection. The connection stays open until
either side sends Connection: close or an
idle timeout expires.
GET /page.html HTTP/1.1
Host: www.example.re
Connection: keep-alive
Persistent connections provide two advantages:
- Reduced handshake overhead: a single three-way handshake serves many requests
- Warmed congestion window: TCP throughput improves over time as the congestion window grows, and keeping the connection open preserves this state
Connection: close
A client or server sends Connection: close
to signal the connection will end after the
current response. HTTP/1.1 clients
not supporting persistence must send
Connection: close with every request.
Servers enforce idle timeouts to reclaim resources. The Keep-Alive header (informal, not standardized) carried timeout and max-request hints in some implementations.
Pipelining (HTTP/1.1)
Pipelining allows sending multiple requests on a persistent connection without waiting for each response. Requests queue on the connection and responses return in the same order.
Pipelining is restricted to idempotent Methods (GET, HEAD, PUT, DELETE). Non-idempotent methods like POST are not safe to pipeline because a failed request with side effects cannot be safely retried.
Head-of-line blocking in pipelining
Head-of-line blocking occurs when a slow response holds up all subsequent responses on the same connection. Because HTTP/1.1 requires responses in request order, a stalled first response blocks every response queued behind. This limitation, combined with inconsistent proxy support, led browsers to disable pipelining.
Domain sharding (HTTP/1.1 workaround)
Browsers limit simultaneous TCP connections per
origin (typically six in HTTP/1.1).
Domain sharding splits resources across multiple
subdomains (img1.example.re, img2.example.re)
to open more parallel connections.
Obsolete with HTTP/2
Domain sharding is counterproductive with HTTP/2. Spreading resources across domains prevents the single-connection multiplexing advantage and forces additional DNS lookups and TLS handshakes.
Multiplexing (HTTP/2)
HTTP/2 replaces pipelining with multiplexing. A single TCP connection carries multiple concurrent streams, each identified by a unique stream ID. Frames from different streams interleave on the connection and are reassembled by the receiver.
Multiplexing solves HTTP-level head-of-line blocking. Responses arrive independently. A slow response on one stream does not block other streams at the HTTP layer.
TCP-level head-of-line blocking
A single lost TCP packet stalls all streams on the connection until retransmission completes. TCP treats the byte stream as ordered, so a gap anywhere blocks delivery of everything queued after. This TCP-layer limitation affects all HTTP/2 streams sharing the connection, and motivated the move to QUIC in HTTP/3.
QUIC and HTTP/3
HTTP/3 replaces TCP with QUIC, a transport protocol built on UDP. QUIC provides its own congestion control, loss recovery, and TLS 1.3 encryption integrated into the transport layer.
QUIC eliminates TCP-level head-of-line blocking. Each QUIC stream is independent at the transport level. A lost packet on one stream only stalls the affected stream. Other streams continue unaffected.
QUIC connections also start faster. The transport handshake and TLS handshake happen together, reducing connection setup to one round trip. Resumed connections with cached keys achieve zero round-trip setup (0-RTT).
Optimistic data and request smuggling
HTTP/1.1 supports in-place protocol transitions through the Upgrade header and the CONNECT method. A client expecting the server to accept the transition sometimes sends new-protocol data immediately, before receiving confirmation. This optimistic behavior reduces latency but introduces a request smuggling risk.
If the server rejects the transition, the optimistic data remains in the connection buffer. An attacker controlling the data source (for example, a malicious website routing traffic through a proxy) crafts payloads resembling valid HTTP requests. The server parses these as real requests, allowing the attacker to issue requests on behalf of the client and bypass security policies.
Mitigating optimistic transitions
Proxy clients forwarding traffic from untrusted
sources must wait for server confirmation (a
2xx response for CONNECT, or
101 Switching Protocols for Upgrade)
before transmitting any data. Alternatively,
sending Connection: close prevents request
smuggling on the same connection.
Connection lifecycle
A typical connection lifecycle across versions:
| Phase | HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|---|
| Setup | TCP handshake per request | TCP handshake once | TCP + TLS handshake once | QUIC handshake (1-RTT or 0-RTT) |
| Reuse | None | Persistent | Multiplexed streams | Multiplexed streams |
| Closure | After each response | Connection: close or timeout |
GOAWAY frame | QUIC CONNECTION_CLOSE |
Example
A persistent HTTP/1.1 connection serving two requests without closing between them:
First request
GET /index.html HTTP/1.1
Host: www.example.re
Connection: keep-alive
First response
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 3720
Connection: keep-alive
Second request (same TCP connection)
GET /style.css HTTP/1.1
Host: www.example.re
Connection: keep-alive
Second response
HTTP/1.1 200 OK
Content-Type: text/css
Content-Length: 1480
Connection: keep-alive
The TCP connection remains open after both
exchanges, available for additional requests
until a timeout or an explicit Connection: close.
Takeaway
HTTP connection management evolved from one-connection-per-request in HTTP/1.0 to persistent connections in HTTP/1.1, multiplexed streams in HTTP/2, and QUIC-based transport in HTTP/3. Each generation reduced latency by minimizing handshakes, improving connection reuse, and eliminating head-of-line blocking at progressively lower layers.
See also
- RFC 9931: Security Considerations for Optimistic Protocol Transitions in HTTP/1.1
- RFC 9112: HTTP/1.1, Section 9 (Connection Management)
- RFC 9113: HTTP/2
- RFC 9114: HTTP/3
- RFC 9000: QUIC
- Connection
- Keep-Alive
- Upgrade
- Alt-Svc
- Protocol Upgrade
- HTTP/0.9
- HTTP/1.0
- HTTP/1.1
- HTTP/2
- HTTP/3
- HTTP headers