Head-of-Line Blocking: Why HTTP/3 Switched From TCP to QUIC

March 30, 2026


Head-of-line blocking is one of those bugs in the design of a protocol that takes decades to fully fix. Each major HTTP version has tried to solve it, and each one has only solved it at one layer of the stack.

HTTP/1.1. A single TCP connection serializes requests. The client sends request A, waits for response A, then sends request B. If response A is slow, B waits. Browsers worked around this by opening six connections per origin in parallel. This helped, but it pushed the problem onto the server (more sockets to manage) and the network (more TCP handshakes, no shared congestion state).

HTTP/2. Multiplexing on a single connection. The client can send requests A, B, and C interleaved on one TCP stream, and responses can come back in any order. At the HTTP layer, head-of-line blocking is gone. You can have many requests in flight without paying for many TCP handshakes.

But HTTP/2 still runs over TCP. And TCP is a single ordered byte stream. If packet 12 is lost on the wire, the kernel will not deliver packets 13, 14, or 15 to the application until packet 12 is retransmitted and arrives, because TCP is required to deliver bytes in order. So your three HTTP/2 streams stall together even though they have no logical relationship.

This is head-of-line blocking at the transport layer. HTTP/2 moved the problem one layer down. It did not eliminate it.

HTTP/3. Built on QUIC, which runs over UDP. QUIC has its own concept of streams, and each stream has its own sequence numbers and its own reliability. If a packet for stream 2 is lost, stream 1 and stream 3 keep delivering. The streams are genuinely independent.

So the three versions correspond to three levels of independence:

  1. HTTP/1.1: one request at a time, on one shared pipe.
  2. HTTP/2: many requests at a time, still on one shared pipe.
  3. HTTP/3: many requests at a time, each on its own pipe.

The performance gap between HTTP/2 and HTTP/3 widens as packet loss goes up. On a perfect network, they are roughly the same. On a flaky mobile network or a long-haul transatlantic link with non-trivial loss, HTTP/3 pulls ahead by a lot.

This is also why HTTP/3 took so long to roll out. Moving the transport from TCP to UDP means rewriting load balancers, firewalls, middleboxes, and every other piece of network plumbing that knew how to look at TCP and did not know how to look at QUIC. The protocol changes were the easy part.

Key takeaway

HOL blocking moves with the bottleneck. HTTP/2 fixed it at the HTTP layer. HTTP/3 had to leave TCP behind to fix it at the transport layer too.

Originally posted on LinkedIn. View original.


All Rights Reserved.