Lesson 9 of 129
HTTP/1.1, HTTP/2, and HTTP/3
HTTP (Hypertext Transfer Protocol) is a request/response protocol: a client sends a request (method, target, headers, optional body) and a server returns a response (status, headers, optional body). The semantics (methods, status codes, headers) are the same across versions. What changed is how messages are encoded and carried over the network, driven by the cost of connections and by head-of-line blocking.
HTTP/1.1
HTTP/1.1 uses a text-based format over TCP.
GET /items/42 HTTP/1.1
Host: api.example.com
Accept: application/jsonKey features:
- Persistent connections (keep-alive): a TCP connection stays open after a response and is reused for later requests. HTTP/1.0 opened a new connection per request, paying the TCP handshake (and TLS handshake) each time. In HTTP/1.1, persistence is the default.
- Host header: required, so one IP address can serve many domains.
- Chunked transfer encoding: a response body can be sent in pieces without knowing its total length in advance.
- Caching and conditional requests:
Cache-Control,ETag,If-None-Match, and range requests.
Limits of HTTP/1.1
- One request at a time per connection. The server sends responses in the order requests arrived, and a connection carries one in-flight exchange. A slow response blocks later requests on that connection: application-level head-of-line blocking.
- Pipelining (sending several requests without waiting for responses) exists in the specification but responses must still return in order, and it had poor support and interoperability problems, so browsers do not enable it.
- Workaround: several connections. Browsers open a limited number of parallel connections per host (commonly six). Sites used domain sharding and asset bundling to get more parallelism. Each connection costs a handshake and its own slow start, and many connections compete with each other for bandwidth.
- Verbose headers are sent uncompressed on every request, including large repeated
CookieandUser-Agentvalues.
HTTP/2
HTTP/2 keeps HTTP semantics but replaces the wire format with a binary framing layer over a single TCP connection (normally with TLS).
- Streams and multiplexing: each request/response pair is a stream with an integer ID. Messages are split into frames (HEADERS, DATA, and others), and frames from different streams are interleaved on one connection. Many requests are in flight at once without waiting for one another, removing HTTP-level head-of-line blocking and the need for many connections.
- Header compression with HPACK: headers are compressed using a static table of common headers, a dynamic table of headers seen earlier on the connection, and Huffman coding. A repeated header is sent as a small table index instead of full text. For example,
:method: GETis entry 2 of the static table and is sent as a single byte. HPACK was designed to avoid the compression side-channel attacks that affected generic compression of headers. - Stream prioritization: clients can express relative priority between streams. In practice, implementations vary, and the original scheme was later deprecated in favor of a simpler approach.
- Flow control per stream and per connection, so a slow consumer of one stream does not consume all buffers.
- Server push: the server could send resources before requested. It saw little successful use, and browsers have removed or disabled support for it.
flowchart LR
subgraph H1["HTTP/1.1: 3 connections"]
direction TB
a1["conn 1: req A"]
a2["conn 2: req B"]
a3["conn 3: req C"]
end
subgraph H2["HTTP/2: 1 connection"]
direction TB
s["streams 1, 3, 5 interleaved as frames"]
endSimplified example: a page needs 60 small resources from one host and the RTT is 50 ms. With HTTP/1.1 and 6 parallel connections, each connection carries about 10 requests one after another, so the transfers need at least 10 round trips (about 500 ms) after connection setup. With HTTP/2, all 60 requests can be sent immediately on one connection, so the transfers need roughly one round trip plus transfer time, provided the network is not lossy.
Remaining problem: TCP head-of-line blocking
All streams share one TCP byte stream. A single lost packet stalls delivery of every stream until retransmission, since TCP delivers bytes in order (see TCP and UDP). On lossy networks, such as mobile links, one HTTP/2 connection can perform worse than several HTTP/1.1 connections.
HTTP/3
HTTP/3 maps HTTP semantics onto QUIC, a transport protocol built on UDP. QUIC is implemented in user space and integrates TLS 1.3.
Properties of QUIC:
- Independent streams: loss on one stream delays only that stream, since QUIC handles ordering and retransmission per stream. This removes transport-level head-of-line blocking.
- Faster connection setup: the transport and TLS handshake are combined, so a new connection needs one round trip before application data (compared with TCP handshake plus TLS handshake in separate steps). A client that has connected before can use 0-RTT resumption to send data in its first flight, with the caveat that 0-RTT data can be replayed by an attacker, so it should only carry safe, idempotent requests.
- Connection IDs and connection migration: a QUIC connection is identified by a connection ID rather than the IP/port 4-tuple, so it survives a client changing networks (for example from Wi-Fi to cellular).
- Always encrypted: packet payloads and most transport metadata are encrypted.
- QPACK replaces HPACK. It is designed so that header compression state does not itself create head-of-line blocking across streams.
- Discovery: clients typically learn a server supports HTTP/3 through an
Alt-Svcresponse header (or an HTTPS DNS record), and connect on UDP, falling back to HTTP/2 over TCP if UDP is blocked.
Costs: user-space implementation uses more CPU than kernel TCP with hardware offloads, and some networks rate-limit or block UDP.
Comparison
| Feature | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Transport | TCP | TCP | QUIC (over UDP) |
| Format | Text | Binary frames | Binary frames |
| Concurrency | One request per connection at a time | Multiplexed streams | Multiplexed streams |
| Header compression | None | HPACK | QPACK |
| HOL blocking | HTTP and TCP level | TCP level | Per stream only |
| Encryption | Optional | Optional in the spec, in practice TLS is used by browsers | Mandatory (TLS 1.3 built in) |
| Connection migration | No | No | Yes |
TLS handshake overview
TLS (Transport Layer Security) provides confidentiality, integrity, and server authentication for a connection. A simplified TLS 1.3 handshake over TCP:
sequenceDiagram
participant C as Client
participant S as Server
C->>S: ClientHello (supported ciphers, key share)
S->>C: ServerHello (chosen cipher, key share), certificate, Finished
C->>S: Finished
Note over C,S: Encrypted application data flows- The client sends its supported cipher suites and a key share for an ephemeral Diffie-Hellman exchange.
- The server picks parameters, sends its own key share, and sends its certificate, signed by a certificate authority the client trusts. The server proves possession of the private key. Both sides derive shared session keys from the key exchange.
- After the client verifies the certificate chain and the
Finishedmessages, both sides send application data encrypted with the session keys.
TLS 1.3 needs one round trip for the handshake (TLS 1.2 needs two). Including the TCP handshake, an HTTPS request over TCP needs about two round trips before the first request byte can be sent; QUIC needs one. Session resumption lets returning clients skip parts of the handshake. See encryption, TLS, and attack mitigation for details.
Choosing and deploying
- Servers and CDNs typically negotiate the best version supported by both sides: HTTP/2 through ALPN (Application-Layer Protocol Negotiation) during the TLS handshake, HTTP/3 through
Alt-Svc. - Internal service-to-service traffic often uses HTTP/2 (for example gRPC) for multiplexing over few long-lived connections.
- Under HTTP/2 and HTTP/3, practices like domain sharding and heavy bundling to reduce request count become less useful.
- A load balancer or proxy may terminate one version on the client side and use another to the backend.
Interview questions
- How does HTTP/2 improve on HTTP/1.1 for page loads? It multiplexes many streams over one connection, compresses headers with HPACK, and avoids opening several connections per host.
- Does HTTP/2 eliminate head-of-line blocking? Only at the HTTP level. Since all streams share one TCP connection, a lost packet still stalls every stream until retransmitted.
- Why is HTTP/3 built on UDP? New transport features (per-stream delivery, integrated TLS, connection migration) are hard to deploy in kernel TCP and through middleboxes. QUIC implements them in user space on top of UDP, which networks pass through.
- What is the risk of 0-RTT data? It can be replayed by an attacker, so servers should accept it only for idempotent requests.
