Skip to content

hop 27 of 32 · 6 min

Three versions of the same protocol

HTTP has been rewritten twice for performance, and both rewrites attack the same problem: too much waiting.

HTTP/1.1

Text based, one request and response per connection at a time.

code
GET /index.html HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
Content-Type: text/html

Keep alive reuses the connection for several requests, avoiding a handshake each time. But they are still strictly sequential: request two waits for response one. That is head of line blocking, and a page needing 80 resources is badly served by it.

The workarounds became standard practice and were all hacks: open six connections per host, shard assets across subdomains to get more, concatenate files into bundles, inline images as data URIs. Every one of those exists to work around one protocol limitation.

HTTP/2

Binary rather than text, and multiplexed: many requests share one connection, interleaved as frames, each with a stream identifier.

code
one connection
  stream 1: GET /index.html   ─┐
  stream 3: GET /style.css     ├─ interleaved, concurrent
  stream 5: GET /app.js       ─┘

Header compression matters more than it sounds. Requests repeat almost identical headers, and cookies can be large; HPACK removes that redundancy.

Server push allowed sending resources before they were asked for. It was hard to use well, usually sent things the browser already cached, and has been removed from browsers.

HTTP/2 does not remove head of line blocking entirely. It removes it at the HTTP layer and TCP remains underneath, so one lost packet stalls every stream until it is retransmitted. On a lossy connection HTTP/2 can be worse than several HTTP/1.1 connections.

HTTP/3

Replaces TCP with QUIC, which runs over UDP and implements reliability itself.

Because each stream is independent inside QUIC, a lost packet affecting one stream does not stall the others. That is the TCP level blocking finally gone.

Two further benefits:

Faster setup. TCP handshake plus TLS handshake is two round trips. QUIC combines them into one, and zero for a resumed connection.

Connection migration. A QUIC connection is identified by a connection identifier rather than by the address and port pair, so moving from WiFi to mobile does not break it. TCP cannot do this, which is why a download fails when you leave the house.

HTTP/1.1HTTP/2HTTP/3
Formattextbinarybinary
Concurrencyone at a timemultiplexedmultiplexed
Head of lineHTTP and TCPTCP onlynone
TransportTCPTCPQUIC over UDP
Setup2 round trips21, or 0 resumed