HTTP/1.1, HTTP/2 and HTTP/3: what changed
HTTP/1.1 is a text protocol over TCP. Main traits:
- Keep-alive (the default): one TCP connection serves many requests so the handshake is not repeated. It can be closed with the
Connection: closeheader; - Head-of-line blocking (HOL): requests on a connection go one after another, a slow response blocks the following ones. Pipelining is not used in practice. Workaround: a browser opens up to ~6 parallel connections per host, and developers once "sharded" domains and bundled files.
HTTP/2 keeps the semantics (methods, codes, headers) but changes the transport:
- Binary frames and multiplexing: many streams (requests) at once in one connection, responses are interleaved. Domain sharding and file bundling become unnecessary;
- HPACK: header compression with a shared table (cookies and
User-Agentare not sent in full every time); - stream priorities; server push is effectively retired (Chrome removed it);
- in practice only over TLS (ALPN
h2).
The problem: HTTP/2 still runs on a single TCP connection, so the loss of one packet stalls all streams (HOL blocking at the TCP level). On bad networks HTTP/2 can be worse than several HTTP/1.1 connections.
HTTP/3 runs over QUIC (UDP, usually port 443). QUIC implements reliability, multiplexing and TLS 1.3 in the protocol itself: a lost packet affects only its own stream; connection setup takes 1 RTT (0-RTT on resumption); connection migration between networks (Wi-Fi → mobile) without a drop; headers are compressed with QPACK. The browser learns about HTTP/3 from Alt-Svc or the HTTPS DNS record; if UDP is blocked, it falls back to HTTP/2.
For QA: the Protocol column in DevTools (http/1.1, h2, h3); curl -v --http2 URL (--http3 only in curl builds that support it); API semantics and test cases do not depend on the version, while performance, behavior under loss and lowercase header names (h2) do.