HTTP/2 — Multiplexing, Streams, HPACK & Push Tradeoffs
HTTP/2 is a binary framed protocol. Many streams share one TCP and TLS connection, HPACK compresses headers, and server push is a dead end in browsers. TCP loss still stalls every stream.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
Question ladder
L1
What is a stream?
Answer
One request and its response, carried as frames that can interleave with other streams on the same connection.
L2
How is that different from HTTP/1.1 pipelining?
Answer
Pipelined responses must finish in order. HTTP/2 frames interleave, so a small stream can complete while a large stream is still open.
L3
Why can HTTP/2 still head-of-line block?
Answer
All streams share one TCP byte stream. A loss stalls retransmission for every stream.
L4
What is HPACK?
Answer
Header compression with a static table and a dynamic table scoped to the connection. QPACK is the HTTP/3 version, built for streams that arrive out of order.
L5
Is server push recommended?
Answer
No for browsers. They removed it. Use preload hints and the CDN.
L6
What does MAX_CONCURRENT_STREAMS do?
Answer
It caps how many streams the peer may open. Above the cap the sender gets RST_STREAM with REFUSED_STREAM and must back off or open another connection.
L7
How does gRPC relate?
Answer
gRPC maps an RPC onto an HTTP/2 stream. Protobuf and streaming shapes stay on the gRPC pages. This page stops at frames, flow control, and GOAWAY.
Failure modes
One loss, every stream
A mobile network drops a packet and the whole HTTP/2 session waits on TCP. Multiplexing did not fail. The transport did.
REFUSED_STREAM after a deploy
The new process advertises a lower stream cap, or it has already sent GOAWAY. Clients that ignore that keep opening streams that will never run.
Push of a cached URL
The server spends bandwidth on a body the browser already stored. Browsers dropped push for this reason.
Header-table secrets
HPACK's dynamic table is connection-scoped. Mixing a secret with attacker-controlled header bytes is a compression-oracle problem. Do not invent a homemade compressor around cookies.
Misconceptions
HTTP/2 is text HTTP with more connections.
It is a binary frame layer. Browsers often use one or two connections per origin because streams provide the concurrency.
Priorities are the original dependency tree.
That tree was hard to implement. Extensible priorities in RFC 9218 replaced it.
h2c is the normal production setup.
The public internet uses HTTP/2 over TLS with ALPN. Cleartext h2c is a special internal case.
Interviewer traps
Teaching protobuf field numbers or streaming RPCs.
Say gRPC uses an HTTP/2 stream, then point at the gRPC versus REST page.
Drawing a mesh control plane to explain GOAWAY.
GOAWAY is a frame. Sidecar operations stay on the mesh page.
Design scenario
Same prompt for every reader.
Requirements
In-flight streams finish or fail cleanly during the deploy. New streams move to the new process. Loss on one response must be understood as TCP HOL, not as an application bug.
Traffic / scale
One or two HTTP/2 connections per browser origin, many streams each. The gRPC client uses a separate pool.
Latency
A small API response can finish while a download is open, until a packet is lost.
Consistency
A refused stream is retried only when the RPC is safe to send again.
Availability
GOAWAY drains the old process before it exits.
Failure assumptions
- Packet loss around one percent on the mobile path.
- The new task advertises a lower MAX_CONCURRENT_STREAMS.
Constraints
- Do not enable browser server push.
- Do not shard the origin to get more HTTP/2 connections.
Prompt
A browser and a gRPC client share an HTTP/2 origin. A rolling deploy drops in-flight browser calls. Mobile users on lossy links see every request stall together.
API
Which frame tells the client to stop opening streams?
Data
What do you log for RST_STREAM and GOAWAY?
Architecture
Where would you try HTTP/3 instead of tuning HTTP/2 further?
What HTTP/2 actually fixed
Prefer
Interleaved streams on one TLS connection
A large download no longer blocks a small JSON response at the HTTP layer. Headers shrink through HPACK. One handshake serves the origin.
- Frames carry a stream id, so responses need not finish in submit order.
- Flow-control windows stop one stream from filling memory.
- Browsers can stay at one or two connections per origin.
Alternative
Assuming the TCP pipe became fair
Loss, congestion, and bufferbloat still belong to one byte stream. Every stream waits together. That gap is the reason to evaluate HTTP/3, not a reason to go back to six HTTP/1.1 sockets.
- One lost packet stalls the connection.
- One congested pipe is the pipe.
- Extra hostnames do not fix loss.
Read an HTTP/2 session from the outside
ALPN selects h2. After that, everything is frames.
- 1
Negotiate h2
On the public internet this is TLS plus ALPN. Cleartext h2c is an internal special case, not the default. - 2
Exchange SETTINGS
Header table size, push enabled or not, max concurrent streams, initial window, max frame size. These are the interview knobs. - 3
Interleave streams
Client-initiated ids are odd. HEADERS and DATA frames from different streams share the connection. RST_STREAM cancels one stream. - 4
Respect windows and GOAWAY
WINDOW_UPDATE is flow control. GOAWAY means stop opening streams, finish the ones below the last id, and reconnect. - 5
Leave push off
SETTINGS_ENABLE_PUSH is 0 in modern browsers. Preload and the CDN replaced it.
Overview
HTTP/2 replaces the text messages of HTTP/1.1 with a binary frame layer. Many streams share one TCP connection and one TLS session. That removes the reason to pipeline and the reason to open six sockets.
It does not remove TCP. Under packet loss the byte stream still delivers in order, so every stream pauses. HTTP/3 is the version that fixes that layer.
gRPC maps each RPC onto a stream and puts protobuf in the DATA frames. The comparison with JSON and GraphQL is gRPC versus REST. Stay on frames here.
Frames and stream ids
| Frame | Role |
|---|---|
| HEADERS | Request or response headers, HPACK-compressed. CONTINUATION extends an oversized block. |
| DATA | Body bytes |
| RST_STREAM | Cancel one stream. REFUSED_STREAM is the code you see when the cap is hit. |
| SETTINGS | Connection knobs, acknowledged by the peer |
| WINDOW_UPDATE | Flow-control credit, per stream and for the connection |
| PING | Liveness that does not require a request |
| GOAWAY | Graceful close. Includes the last stream id the sender will process. |
| PUSH_PROMISE | Server-initiated stream. Unused in current browsers. |
Stream 0 is the connection itself. Client-opened streams use odd ids, starting at 1. Even ids were for server push.
Browsers typically open one or two HTTP/2 connections per origin, because streams already multiplex. That makes the congestion window and loss on that single TCP pipe the whole story. Opening more hostnames to get more connections fights the protocol. CDNs may still use more than one connection for their own reasons. Do not copy sharding forward from HTTP/1.1.
Decisions
- 1
One TLS connection
- nextStream 1 data
- 2
Stream 1 data
- nextStream 3 interleaved
- 3
Stream 3 interleaved
- nextSETTINGS and GOAWAY
- 4
SETTINGS and GOAWAY
- nextPacket lost?
- ?
Packet lost?
- YesTCP stalls all streams
- NoStreams stay independent
- 6
TCP stalls all streams
- 7
Streams stay independent
Lesson map
HTTP/2 — Multiplexing, Streams, HPACK & Push Tradeoffs
HTTP/2 is a binary framed protocol. Many streams share one TCP and TLS connection, HPACK compresses headers, and server push is a dead end in browsers. TCP loss still stalls every stream.
Architecture. Architecture
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB a["One TLS connection"] b["Stream 1 data"] c["Stream 3 interleaved"] d["SETTINGS and GOAWAY"] a -->|One TLS connection| b b -->|Stream 1 data to Stream 3 interleaved| c c -->|Stream 3 interleaved| d
| Layer | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Application HOL | Yes | No | No |
| Transport HOL | TCP | TCP | QUIC, per stream |
HPACK
HPACK (RFC 7541) keeps a static table and a dynamic table so repeated headers shrink. The dynamic table belongs to one connection. That is why a proxy cannot casually rearrange header blocks across connections, and why a secret that shares compressor state with attacker-controlled bytes is a bad idea. This is the same family of problem as CRIME and BREACH: compression plus a secret plus attacker influence.
Practical rule: use a modern TLS stack, do not compress secrets together with headers an attacker can set, and do not write your own header codec. QPACK exists because HPACK assumes a total order the QUIC stream model does not give you. One sentence is enough until the next lesson.
Pros: large savings when cookies and user-agent repeat.
Cons: a connection-scoped table, and a security footgun if you treat compression as free.
Server push
Push let the server open a stream for a URL the client had not asked for. The server did not know the cache. The client often threw the bytes away. Chrome removed HTTP/2 and HTTP/3 push. SETTINGS_ENABLE_PUSH is 0.
Prefer preload hints and a cache you can reason about. Hierarchy and keys stay on CDN cache hierarchy.
Flow control and priorities
Each stream has a window, and the connection has a window. A fast sender must wait for WINDOW_UPDATE before it can queue more DATA. That is how one upload fails to exhaust the peer's memory.
The original priority tree, with exclusive dependencies and weights, proved too hard. RFC 9218 replaces it with extensible priorities: an urgency and a boolean for incremental delivery. If an interviewer mentions the old tree, say it lost and name the RFC.
SETTINGS worth memorizing
SETTINGS_HEADER_TABLE_SIZEbounds the HPACK dynamic table.SETTINGS_ENABLE_PUSHis 0 where browsers are the client.SETTINGS_MAX_CONCURRENT_STREAMSis the backpressure cap. Exceeding it yieldsREFUSED_STREAM.SETTINGS_INITIAL_WINDOW_SIZEis the starting flow-control window.SETTINGS_MAX_FRAME_SIZEcaps a frame payload. The default is 16 KiB.
GOAWAY and deploys
Before a process exits it should send GOAWAY so clients stop opening streams and finish the ones already accepted. Clients that ignore it keep sending on a dying socket. Clients that honor it open a new connection. Pool behavior for that race is the connections lesson. Mesh access logs that surface the reset are the sidecar page, not a second control-plane lecture.
h2c versus TLS
Production internet paths are HTTP/2 over TLS with ALPN h2. Cleartext HTTP/2 (h2c), including the prior-knowledge form some gRPC meshes use, is a special case. If policy requires encryption, put it on another hop. Do not treat h2c as the thing you enable "because the spec allows it."
Debug with curl --http2 -v. curl --http2-prior-knowledge is the h2c form. Chrome netlog export shows stream state when a browser is the client.
Sandbox: who finishes first
Press Run. Snippets must be self-contained — no network, files, or native modules.
Press Run. Snippets must be self-contained — no network, files, or native modules.
Four streams share an HTTP/2 connection. A single TCP segment is lost. Which streams stall, and what changes if those streams were QUIC streams instead? You do not need the QUIC packet layout. You need the delivery rule.
Interview Q&A
How does HTTP/2 multiplexing differ from HTTP/1.1 pipelining?
Answer
Pipelining writes several requests and then requires responses in the same order. HTTP/2 frames are tagged with a stream id and may interleave. A small stream can finish while a large stream is still sending DATA.
Why can HTTP/2 still head-of-line block?
Answer
Every stream is carved out of one TCP byte stream. TCP delivers that byte stream in order. Until the lost segment is retransmitted, no stream can make progress. QUIC gives each stream its own reliability.
What is HPACK?
Answer
HTTP/2 header compression. A static table covers common header names. A dynamic table, scoped to the connection, covers repeats such as cookies. QPACK is the HTTP/3 codec, designed so header blocks do not require in-order delivery across streams.
Is server push recommended?
Answer
No for browsers today. The server could not see the cache, so it often pushed bytes the client discarded. Chrome removed push. Use preload and the CDN.
How does gRPC relate to this page?
Answer
A gRPC call is an HTTP/2 stream with a protobuf body and its own status trailer. Deadlines, streaming shapes, and load-balancing policy live on the gRPC pages. If the question is frames, GOAWAY, or HPACK, stay here.
What does SETTINGS_MAX_CONCURRENT_STREAMS do?
Answer
It caps parallel streams. Past the cap the peer resets the stream with REFUSED_STREAM. A pool treats that as backpressure: wait, or open another connection if the deployment really needs one. It is not a reason to disable HTTP/2.
Why binary framing?
Answer
Length-prefixed frames are unambiguous to split, cheap to multiplex, and harder to desync than text. Humans debug them with curl -v and netlog, not by reading the socket as ASCII. That is a real cost. It is why HTTP/1.1 remains the debug fallback.