Under HTTP/1.1, a connection handles one request at a time. A slow response blocks everything behind it — head-of-line blocking — so browsers worked around it by opening six connections per origin and praying. HTTP/2 replaces the hack with real multiplexing. Then it runs into a wall TCP built.
HTTP/1.1’s Dilemma
One request, one response, in order, per connection:
conn: [── req A ──][ wait ][── req B ──][── req C ──]
└─ B and C stall behind a slow AThe six-connection workaround multiplies overhead — six TLS handshakes, six slow starts, six congestion windows — and the browser limit means request seven still waits.
Streams and Frames
HTTP/2 splits the single connection into many independent streams, each a request/response pair. Messages are chopped into frames tagged with a stream ID, interleaved on the wire, and reassembled on the other end:
one TCP connection
┌──────────────────────────────────────────────┐
│ [S1 hdr][S3 data][S1 data][S5 hdr][S3 data]… │
└──────────────────────────────────────────────┘
frames from different streams, interleavedNow a slow response on stream 1 doesn’t block stream 3 — their frames just interleave. One connection, one handshake, one warm congestion window, and effectively unlimited concurrent requests. The application-layer head-of-line problem is gone.
The Header Win
HTTP headers are repetitive — the same User-Agent, Accept, and cookies on
every request. HTTP/2 adds HPACK, which keeps a shared dynamic table of
previously sent headers so repeats become small references instead of full text.
On a page firing 100 requests, that’s a real saving.
Where TCP Bites Back
Here’s the twist: HTTP/2 multiplexes streams, but they all ride one TCP connection, and TCP guarantees in-order byte delivery. If a single packet is lost, TCP holds back every stream’s bytes until the retransmission arrives — even streams that were complete:
packet for S1 lost → TCP buffers everything after it
S3, S5 data sits ready but undeliveredThe head-of-line blocking moved down a layer, from HTTP to TCP. On a clean network you never notice; on a lossy mobile link it hurts.
This is exactly the problem HTTP/3 solves by moving to QUIC over UDP, where each stream has independent delivery and one lost packet only stalls its own stream.
Takeaways
- HTTP/2 carries many independent streams over one connection by interleaving frames, killing application-layer head-of-line blocking.
- One connection means one handshake, one congestion window, and HPACK header compression — a big win over HTTP/1.1’s connection-juggling.
- TCP’s in-order delivery reintroduces head-of-line blocking on packet loss, which is the motivation for HTTP/3 over QUIC.