HTTP/2 Deep Dive#
HTTP/2 replaces HTTP/1.1's per-request connection model with a single binary, multiplexed connection. Many requests share one TCP socket; framing lets them interleave; HPACK compresses headers.
sequenceDiagram
participant C as Client
participant S as Server
C->>S: open one TCP connection + TLS
par stream 1
C->>S: HEADERS GET /a
S->>C: HEADERS + DATA /a
and stream 3
C->>S: HEADERS GET /b
S->>C: HEADERS + DATA /b
and stream 5
C->>S: HEADERS GET /c
S->>C: HEADERS + DATA /c
end
Each request gets its own stream id; frames carry the stream id so the receiver can demultiplex. Solved HTTP/1.1's head-of-line blocking at the HTTP layer (TCP-level HOL still exists; that's HTTP/3's job).
HTTP/2 (RFC 7540, 2015) keeps HTTP semantics identical but rewrites the wire format. The protocol is binary, multiplexed, and stateful at the connection level.
Frame structure#
+-----------------+
| Length (24) |
+-----------------+
| Type (8) |
+-----------------+
| Flags (8) |
+-----------------+
| R| Stream Id (31)|
+-----------------+
| Frame Payload |
+-----------------+
Every frame belongs to a stream. Common types:
| Type | Purpose |
|---|---|
DATA |
Request/response body |
HEADERS |
Request/response headers |
SETTINGS |
Connection-level config |
WINDOW_UPDATE |
Flow control credit |
PUSH_PROMISE |
Server push declaration |
RST_STREAM |
Abort one stream |
PING |
Liveness and RTT |
GOAWAY |
Connection shutdown |
Multiplexed streams#
flowchart TB
subgraph Connection[Single TCP+TLS connection]
direction LR
S1[Stream 1<br/>HEADERS, DATA, DATA] -.frames.-> Wire
S3[Stream 3<br/>HEADERS, DATA] -.frames.-> Wire
S5[Stream 5<br/>HEADERS] -.frames.-> Wire
Wire[Multiplexed frame stream]
end
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
Client opens stream ids 1, 3, 5, ... (odd). Server opens even-numbered streams (for push). Frames from different streams interleave; the receiver assembles per stream.
HPACK header compression#
HTTP headers are repetitive (Host, Accept, User-Agent, cookies). HPACK combines:
- Static table of 61 common headers.
- Dynamic table of recently seen headers.
- Huffman encoding of literal values.
Result: a 1KB header set often compresses to <100 bytes.
:method GET # static table index 2
:path /api/users # dynamic + Huffman
authorization Bearer eyJ... # dynamic + Huffman
HPACK was the answer to CRIME-style compression attacks on TLS-compressed HTTP/1.
Flow control#
Per-stream and per-connection windows (default 64KB):
WINDOW_UPDATE stream_id=3 delta=32768 # I can receive 32KB more on stream 3
WINDOW_UPDATE stream_id=0 delta=32768 # connection-level
Recipient must consume bytes before issuing more credit. Prevents a fast sender from overwhelming a slow stream.
Prioritisation#
Each stream declares a priority: a weight and an optional dependency on another stream. Servers use these to schedule frame output. The original priority scheme in RFC 7540 was complex and largely abandoned; RFC 9218 introduced the simpler Priority header (urgency 0-7, incremental flag).
Server push#
Server proactively sends resources the client will need. In practice, server push has been deprecated in Chrome and most CDNs in favor of 103 Early Hints, because it was hard to predict cache state.
Connection coalescing#
If a client has an HTTP/2 connection to a.example.com and is about to open one to b.example.com, and the TLS cert covers both, the browser may reuse the existing connection. Saves a handshake per origin.
Head-of-line blocking - still there at the TCP layer#
flowchart LR
S1[Stream 1] --> Wire
S2[Stream 2] --> Wire
Wire --> TCP[TCP segment]
TCP -. one packet lost .-> Drop
Drop -. stalls all streams .-> S1
Drop -. stalls all streams .-> S2
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
HTTP/2 fixed HOL at the HTTP layer, but a lost TCP segment still stalls every multiplexed stream until retransmit. QUIC (HTTP/3) finally solves this by replacing TCP.
Operational notes#
- Connection longevity: HTTP/2 connections live for minutes/hours. Load balancers must handle long-lived L7 connections.
- Single connection per origin: solves the 6-connection limit of HTTP/1.1 but concentrates load. Make sure LBs distribute streams.
- Slow start: TCP slow start applies; new connections are slow for the first second.
Where HTTP/2 fits#
flowchart TB
H2((HTTP/2))
HTTP[HTTP protocols<br/>parent comparison]
TCP[TCP deep dive<br/>the transport]
H3[HTTP/3 + QUIC<br/>successor]
GRPC[gRPC mechanics<br/>top H2 user]
HTTP --> H2
TCP --> H2
H2 -. evolves to .-> H3
H2 --> GRPC
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class H2 service;
class HTTP,TCP,H3,GRPC datastore;
Glossary & fundamentals#
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
HTTP protocols | the overview page | http-protocols |
HLD |
TCP deep dive | underlying transport | tcp-deep-dive |
HLD |
HTTP3 QUIC | TCP HOL fix | http3-quic |
HLD |
gRPC mechanics | top consumer of HTTP/2 | grpc-mechanics |
Quick reference#
Key changes from HTTP/1.1#
- Binary framing instead of text
- Stream multiplexing on one TCP connection
- HPACK header compression
- Server push (now deprecated, use 103 Early Hints)
- Flow control per stream and connection
Frame types#
DATA, HEADERS, SETTINGS, WINDOW_UPDATE, PUSH_PROMISE, RST_STREAM, PING, GOAWAY.
Streams#
Client streams: 1, 3, 5, ... (odd) Server-pushed streams: 2, 4, 6, ... (even)
HPACK#
Static table + dynamic table + Huffman; typical compression 90%+ on repeated headers.
Flow control#
WINDOW_UPDATE per stream and connection. Default 64KB window. Tune up for high-BDP.
Prioritization#
Original tree-based scheme deprecated. RFC 9218 Priority header (urgency 0-7, incremental).
Limitation#
TCP-level HOL: one lost segment blocks every multiplexed stream until retransmit. Fixed by HTTP/3.
Tools#
nghttp2,h2loadfor testing- Chrome
chrome://net-export/for traces curl --http2for client experiments
Refs#
- RFC 9113 - HTTP/2
- RFC 7541 - HPACK
- web.dev HTTP/2 introduction
FAQ#
What problem does HTTP/2 solve over HTTP/1.1?#
HTTP/2 removes HTTP-layer head-of-line blocking by multiplexing many requests over one TCP connection, and compresses headers with HPACK to cut bytes on the wire.
How does HTTP/2 multiplexing work?#
Each request gets a stream ID, and frames carry that ID so the receiver can interleave and demultiplex many in-flight requests on a single TCP connection.
Does HTTP/2 still suffer from head-of-line blocking?#
Yes, at the TCP layer. If a packet is lost, all HTTP/2 streams stall until retransmission. HTTP/3 over QUIC fixes this by running streams over UDP.
What is HPACK in HTTP/2?#
HPACK is a stateful header compression scheme that uses static and dynamic tables of common header fields to avoid resending the same headers on every request.
Is HTTP/2 server push still used?#
Rarely. Most browsers and CDNs have deprecated server push because cache-aware preload links proved simpler and more effective in practice.
Related Topics#
- HTTP Protocols: the parent topic comparing all three versions
- TCP Deep Dive: the transport HTTP/2 sits on
- gRPC Mechanics: runs exclusively on HTTP/2
Further reading#
- RFC - RFC 9113 - HTTP/2
- RFC - RFC 7541 - HPACK
- Doc - Google - HTTP/2 introduction
- Blog - Cloudflare - Tools for HTTP/2 debugging