Guide contents

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.

Text
GET /items/42 HTTP/1.1
Host: api.example.com
Accept: application/json

Key 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 Cookie and User-Agent values.

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: GET is 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"]
    end

Simplified 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-Svc response 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
  1. The client sends its supported cipher suites and a key share for an ephemeral Diffie-Hellman exchange.
  2. 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.
  3. After the client verifies the certificate chain and the Finished messages, 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.