Octet

Explain head-of-line blocking in HTTP/1.1, HTTP/2 and HTTP/3. Which layer does each version fix?

Show the answer

Say this first

Head-of-line blocking is when one slow or lost item holds up everything queued behind it. HTTP/1.1 has it at the application layer: responses on a connection come back in request order. HTTP/2 fixes that by multiplexing streams, but moves the problem down to TCP: TCP delivers bytes strictly in order, so one lost segment stalls every stream on the connection until it is retransmitted. HTTP/3 fixes the transport layer too, because QUIC orders data per stream: a loss only stalls the stream it belongs to.

Go deeper

HTTP/1.1

Pipelining lets a client send several requests without waiting, but responses must return in order, so a slow first response blocks the rest. Browsers never enabled it and open about six parallel connections instead.

HTTP/2

Frames from many streams interleave on one TCP connection. The application is no longer blocked, but the kernel will not hand over bytes after a gap. With 1% loss on a busy page, one gap freezes every image, script and API call on the connection for a round trip or more.

HTTP/3 and QUIC

QUIC numbers packets, not bytes of one stream. Each stream has its own offsets, so data for stream 8 can be delivered while stream 4 waits for a retransmission. Order is only kept inside a stream.

The header compression detail

HPACK's dynamic table assumes in-order delivery: every header block may refer to earlier ones. QPACK was designed so that header decoding does not reintroduce cross-stream blocking, using separate encoder and decoder streams and letting the encoder limit how much a stream may depend on unacknowledged table entries.

Traps

  • Saying HTTP/2 solved head-of-line blocking. It solved the HTTP-level kind and exposed the TCP-level kind.