HTTP/1.1: The Original

Released 1997. Still alive. Plain text, request-response, one request at a time per connection.

To improve speed, browsers open multiple parallel connections (typically 6 per domain) and use persistent connections (reuse the same TCP connection for many requests). Pipelining (sending requests without waiting for responses) was specified but never reliably implemented.

The Head-of-Line Blocking Problem

HTTP/1.1's biggest flaw: requests on the same connection are serialized. If request 1 takes 5 seconds, requests 2 and 3 wait. Even on different connections, browsers limit parallelism per domain.

Workarounds were ugly: spritesheets to combine images, domain sharding to bypass connection limits, inlining CSS/JS, concatenating files. All to dodge a protocol limitation.

HTTP/2: Multiplexing

Released 2015. Same semantics as HTTP/1.1 (methods, status codes, headers) but a new wire format.

The big change: multiplexing. Many requests and responses can interleave on a single TCP connection. Each request gets a stream ID. Frames from different streams can be sent in any order, then reassembled at the destination.

Other improvements:

Binary framing: faster to parse than text.
HPACK header compression: repeated headers (cookies, user agent) compressed to a few bytes.
Server push: server can send resources the client hasn't asked for yet (mostly unused in practice; deprecated).
Priority hints: client can suggest which streams matter most.

HTTP/2 made many of the HTTP/1 workarounds unnecessary. Concatenating JS bundles, for example, became less important.

The TCP Problem

HTTP/2 still runs over TCP. TCP itself has its own head-of-line blocking: if a packet is lost, the kernel waits for retransmission before delivering later packets, even if those later packets are for different HTTP/2 streams.

The protocol no longer blocks. The transport does. On unreliable networks (mobile especially), this is a real problem.

HTTP/3: New Transport

Released 2022. The semantics stay the same; the transport changes.

HTTP/3 replaces TCP with QUIC, a new transport protocol built on UDP. QUIC includes its own:

Connection establishment (faster than TCP+TLS).
Reliability (retransmissions, ordering).
Congestion control.
Built-in encryption (TLS 1.3 baked in).
Independent streams (no shared head-of-line blocking).

Each HTTP/3 stream has its own ordering. A packet loss on stream 1 doesn't delay stream 2.

Why UDP?

QUIC needed to ship without OS upgrades. TCP is implemented in the kernel; changing it requires every device to update. UDP is "send a packet, hope it arrives" with everything else built on top in user space. QUIC builds reliability and ordering on top of UDP, which means it ships in browsers and servers without kernel changes.

Connection Setup

Old way: TCP handshake (1 RTT) + TLS handshake (2 RTT in TLS 1.2, 1 RTT in TLS 1.3) = several round trips.
HTTP/2 over TLS 1.3: 2 RTT before first request.
HTTP/3 (QUIC): 1 RTT for fresh connection. 0 RTT for resumed connections (data sent in the very first packet).

For mobile users with high latency, this is a meaningful win.

Connection Migration

QUIC connections survive network changes. Switch from WiFi to cellular mid-download? TCP would have to redo everything. QUIC keeps the connection alive (the connection is identified by a connection ID, not source IP+port).

Comparison

HTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC (UDP)
FormatTextBinaryBinary
MultiplexingNoYesYes (independent)
Header CompressionNoHPACKQPACK
Connection Setup3+ RTT2-3 RTT1 RTT (0 RTT resumed)
HoL Blocking at TransportYesYesNo
Network MigrationNoNoYes

Should You Use HTTP/3?

For public-facing sites: yes if your CDN supports it (Cloudflare, Fastly, CloudFront). Most users see modest but real performance gains, especially mobile.

For internal services: HTTP/2 is fine. The QUIC benefits matter mostly when network is unreliable.

The One Thing to Remember

Each HTTP version solved a specific bottleneck. HTTP/1.1's was head-of-line blocking, HTTP/2 fixed it but TCP's HoL remained, HTTP/3 swapped TCP for QUIC to fix that too. The actual semantics (methods, headers, status codes) haven't changed since 1997. As a developer, you mostly don't care which version your stack uses; just enable the latest your CDN supports and move on.