Protocol Deep Dives

RFC 7540: HTTP/2 Protocol Deep Dive

A technical deep dive into RFC 7540 — the HTTP/2 standard — covering binary framing, stream multiplexing, HPACK header compression, server push, flow control, and how HTTP/2 compares to HTTP/3.

Why HTTP/2?

HTTP/1.1 (1997) was designed when a web page loaded a handful of resources. A modern page might request 100+ assets. HTTP/1.1's serial request model, text-based framing, and redundant headers create enormous overhead:

  • Head-of-line blocking — later requests wait for earlier ones
  • Repeated headersUser-Agent, Cookie, Accept sent identically on every request
  • 6-connection limit — browsers work around blocking with parallel connections

HTTP/2 (RFC 7540, 2015 — superseded by RFC 9113 in 2022) was developed from Google's SPDY protocol and addresses all three problems with a single TCP connection.

Binary Framing Layer

The most fundamental change in HTTP/2: the wire format is binary, not text. All communication happens through fixed-format frames.

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                        |
+---------------------------------------------------------------+

Every HTTP/2 frame has a 9-byte fixed header: length (up to 16 MB), type, flags, and stream ID. The binary format eliminates the need to parse variable-length text lines — parsers become deterministic and faster.

Frame types:

TypeCodePurpose
DATA0x0Request/response body
HEADERS0x1Request/response headers
PRIORITY0x2Stream priority (deprecated in RFC 9113)
RST_STREAM0x3Terminate a stream immediately
SETTINGS0x4Connection parameters
PUSH_PROMISE0x5Server push announcement
PING0x6Connection keepalive
GOAWAY0x7Graceful connection shutdown
WINDOW_UPDATE0x8Flow control
CONTINUATION0x9Header continuation

Streams and Multiplexing

A stream is an independent, bidirectional sequence of frames within a connection. Each request-response pair is one stream.

Connection:
  Stream 1: GET /api/user ────► 200 OK + body
  Stream 3: GET /api/orders ───► 200 OK + body
  Stream 5: GET /api/prefs ────► 200 OK + body
  (all interleaved on one TCP connection, no waiting)

Stream IDs are odd for client-initiated streams and even for server-push streams. Streams progress through states: idle → open → half-closed → closed. RST_STREAM can terminate any stream immediately without closing the connection.

Multiplexing eliminates head-of-line blocking at the HTTP layer. (Note: TCP HoL blocking still exists in HTTP/2; HTTP/3 solves this by replacing TCP with QUIC.)

Header Compression (HPACK)

RFC 7541 defines HPACK, the header compression format for HTTP/2. It reduces header overhead by 85–95% compared to HTTP/1.1.

HPACK uses three techniques:

  • Static Table — 61 pre-defined header name/value pairs (e.g., method: GET, status: 200, content-type: text/html). Referenced by a single integer.
  • Dynamic Table — a FIFO list of headers seen in the current connection. Subsequent requests can reference past headers by index instead of repeating them.
  • Huffman Encoding — new header values are Huffman-compressed before transmission.
First request — full headers transmitted
Second request — User-Agent, Accept, Cookie referenced by index
                 Only changed headers (e.g., :path) sent as new strings

Server Push

HTTP/2 server push allows the server to proactively send resources the client will need, before the client requests them. The server sends a PUSH_PROMISE frame announcing the upcoming resource, followed by the resource on an even-numbered stream:

Client: GET /index.html (Stream 1)
Server: PUSH_PROMISE Stream 2 → /styles.css
Server: PUSH_PROMISE Stream 4 → /app.js
Server: HEADERS + DATA Stream 1 → index.html
Server: HEADERS + DATA Stream 2 → styles.css
Server: HEADERS + DATA Stream 4 → app.js

In practice, server push has seen limited adoption — browsers often already have resources cached, wasting bandwidth. The 103 Early Hints status code is now preferred for hinting at subresources.

Flow Control

HTTP/2 has two levels of flow control to prevent fast senders from overwhelming slow receivers:

  • Connection-level — limits total DATA frame bytes in flight
  • Stream-level — limits bytes per individual stream

Each side maintains a receive window and advertises it via WINDOW_UPDATE frames as it consumes data. Default initial window size: 65,535 bytes. For high-throughput APIs, increase it via SETTINGS:

SETTINGS_INITIAL_WINDOW_SIZE = 1048576  # 1 MB

HTTP/2 vs HTTP/3

AttributeHTTP/2HTTP/3
TransportTCPQUIC (UDP-based)
HoL blockingAt TCP levelEliminated
Connection setup1–2 RTT (TLS 1.3)0–1 RTT
Header compressionHPACKQPACK
Connection migrationNoYes (QUIC CID)
SpecRFC 7540 / 9113RFC 9114

HTTP/3 eliminates TCP HoL blocking entirely by using QUIC's independent stream delivery. A lost UDP packet only delays the specific stream it belongs to, not the entire connection.

Giao thức liên quan

Thuật ngữ liên quan

Thêm trong Protocol Deep Dives