Head-of-Line Blocking (HTTP/1.1 vs HTTP/2 vs HTTP/3)
Web developmentweb development / networking
The myth
"We moved to HTTP/2, so head-of-line (HOL) blocking is gone — one connection multiplexes everything, problem solved."
Actually…
HTTP/2 fixed HOL blocking at the application layer but pushed it down to the transport (TCP) layer. On a lossy network it can be just as painful. HTTP/3 is what actually removes it — by ditching TCP.
The right mental model (3 stages)
- HTTP/1.1 — HOL at the request level
One request per connection at a time. Browsers open ~6 parallel TCP connections per origin to fake concurrency. A slow response blocks its own connection; the others keep going.
- HTTP/2 — one connection, many streams
All requests share ONE TCP connection, chopped into interleaved frames (streams). App-layer HOL gone: a slow response no longer blocks the others logically.
BUT TCP delivers bytes strictly in order. If ONE packet is lost, TCP holds back ALL later bytes until the retransmit arrives — even bytes belonging to unrelated streams. So every stream stalls on one lost packet. That's TCP-level HOL blocking.
- HTTP/3 — QUIC over UDP
QUIC runs on UDP and makes each stream independently ordered. A lost packet only stalls the stream that owned those bytes; the rest keep flowing.
A concrete number
Say 1% packet loss, 100 in-flight streams.
- HTTP/2: that one loss stalls the whole connection → all 100 streams wait for the retransmit (~1 RTT of dead time, e.g. 100ms on mobile).
- HTTP/3: only the 1 affected stream waits; 99 keep going.
On a clean wired network (~0% loss) HTTP/2 and HTTP/3 look nearly identical. The gap shows up on flaky Wi-Fi / cellular.
The scenario that trips people up
You benchmark HTTP/2 in the office on gigabit fiber → looks great, ship it. Real users on 4G with 2% loss report the site feels slower under load than your old HTTP/1.1 (6 connections) setup, because now a single dropped packet freezes every asset at once. The fix isn't "tune HTTP/2" — it's HTTP/3.
Practical takeaways
- HTTP/2's win is mainly fewer connections + header compression + multiplexing on GOOD networks.
- The bigger real-world win of HTTP/3 is resilience to loss (and 0-RTT connection setup / connection migration across IP changes).
- HTTP/3 needs UDP open on the path; some corporate networks block it → clients fall back to HTTP/2. Always keep the fallback working.
- Bonus: QUIC also removes the TCP+TLS handshake round trips (1-RTT, often 0-RTT resumes).
Common mistakes
- Assuming multiplexing = no blocking. It removes app-layer HOL, not transport HOL.
- Benchmarking only on a perfect network, where the difference is invisible.
- Forgetting HTTP/3 can silently fall back to H2, so measure what clients actually negotiate.
Your exercise
Open Chrome DevTools → Network tab → right-click the column header → enable "Protocol". Load a big site (e.g. a CDN-heavy one) and note which requests show h2 vs h3. Then in a terminal try:
curl -sI --http3 https://cloudflare.com | head -1
curl -sI --http2 https://cloudflare.com | head -1
Which protocol did your browser negotiate for most assets — and can you find one origin that only offers h2?