Networking and resource loading

The HTTP/2 protocol

Learn how HTTP/2 uses streams, multiplexing, HPACK, flow control, and binary framing, how it is negotiated, and how it differs from HTTP/3.

I suggest reading the HTTP tutorial first.

HTTP/2 is one of the versions of the HTTP protocol used on the web. It was standardized in 2015 as RFC 7540, replaced in 2022 by RFC 9113, the current spec.

HTTP/1.1 is still in use, and HTTP/3 is deployed too. A client and a server use the newest version they both support.

HTTP/2 keeps the same HTTP semantics. Methods, status codes, URLs, headers, and bodies mean the same things. What changes is how the messages are framed and transported.

Its main features are:

  • streams and request/response multiplexing
  • binary framing
  • HPACK header compression
  • per-stream and connection-level flow control
  • stream prioritization
  • optional server push

How HTTP/2 is negotiated

Over HTTPS, client and server pick HTTP/2 during the TLS handshake using ALPN, a TLS extension that lets both sides agree on a protocol before any HTTP data flows. The identifier is h2.

If HTTP/2 isn’t available they fall back to HTTP/1.1. That’s why enabling it on a server is transparent to your application code, even though the bytes on the wire look completely different.

The spec also defines cleartext HTTP/2, h2c, but browsers only use HTTP/2 over TLS.

Streams and multiplexing

An HTTP/2 connection contains many independent streams. Each request and response gets its own stream, and frames from different streams are interleaved on the same TCP connection.

With HTTP/1.1, browsers opened several TCP connections because a slow response blocked everything behind it. HTTP/2 lets many requests progress at once over one connection. Old workarounds like domain sharding became counterproductive, and concatenating files to cut requests matters less.

Multiplexing has a limit. All streams share one TCP connection, so a lost packet stalls every stream while TCP recovers it. This is head-of-line blocking at the transport level.

Binary framing

HTTP/2 splits messages into binary frames. HEADERS frames carry compressed headers, DATA frames carry the body. Every frame has a stream identifier, so the receiver can reassemble interleaved data into the right response.

Binary framing doesn’t change your HTML or JSON. And it’s not encryption. TLS handles that.

Header compression with HPACK

Requests and responses repeat the same header names and values over and over. Cookies make them large.

HTTP/2 compresses headers with HPACK, defined in RFC 7541. Encoder and decoder share a table, so a repeated header goes out as a small reference instead of the full text.

Flow control and prioritization

Flow control stops a sender from overwhelming the receiver. HTTP/2 applies it per stream and to the whole connection.

Prioritization lets an endpoint say which streams matter more. The original scheme from RFC 7540 was deprecated by RFC 9113. The replacement is RFC 9218.

What happened to server push?

Server push let a server send a response before the client asked for it. It’s still optional in the spec, but hard to use well: the server easily pushes something already cached, or something the page never uses.

Chrome removed HTTP/2 server push in version 106. For performance work, use preload links and 103 Early Hints instead.

HTTP/2 and HTTP/3

HTTP/3 was standardized in 2022 as RFC 9114.

HTTP/2 runs over TCP. HTTP/3 runs over QUIC, a transport built on UDP with security built in. QUIC streams are independent at the transport level, so a lost packet on one stream doesn’t block the others. That fixes the head-of-line blocking we saw above. HTTP/3 uses QPACK instead of HPACK for the same reason.

HTTP/2 is still useful and widely deployed. HTTP/3 improves the transport, it doesn’t make HTTP/2 obsolete.

Lesson completed