HTTP/2: multiplexing the same Web semantics
HTTP/2 replaced ordered text exchanges with compressed, multiplexed streams while preserving HTTP methods, statuses, fields, and URLs.
- HTTP history
- HTTP/2
- multiplexing
- binary framing
- HPACK
- ALPN
- performance
HTTP/2 targeted connection use, not HTTP’s application model
HTTP/1.1 made persistent connections the default, but a browser still faced an awkward concurrency choice. It could pipeline requests and accept ordered responses, or open several TCP connections. Pipelining was fragile and suffered application-layer head-of-line blocking. Multiple connections worked more reliably but duplicated handshakes, congestion state, buffers, and competition for network capacity.
At the same time, request and response fields had become repetitive. A page load could send similar cookies, user-agent information, accepted formats, and cache validators on many requests. Text field syntax made those values easy for people to inspect but inefficient to repeat.
HTTP/2 addressed these connection-level costs. It did not introduce a new URL
scheme, replace GET with another retrieval operation, or redefine successful
and failed responses. Its central design was an optimized expression of existing
HTTP semantics.
SPDY became a starting point, not the finished standard
Google began experimenting with SPDY as a lower-latency protocol for Web traffic. It demonstrated several ideas that shaped HTTP/2: one connection with multiplexed streams, compressed request metadata, prioritization, and server-initiated responses.
When the IETF HTTP working group began HTTP/2 work in 2012, it selected SPDY as the starting point. The IETF’s publication announcement records that the group then worked through more than 200 design issues, 17 drafts, six interim meetings, and around 30 implementations. The resulting protocol changed framing, compression, and its mapping to HTTP semantics.
This distinction matters. Calling HTTP/2 “SPDY standardized” understates the interoperability process and the design changes produced by independent review and implementation.
RFC 7540 and the separate HPACK specification, RFC 7541, were published in May 2015. RFC 9113 replaced RFC 7540 in 2022 after deployment experience exposed features that needed clarification, deprecation, or tighter security guidance.
Binary frames replaced the textual message boundary
HTTP/1.1 represents a message as a start line, field lines, delimiters, and possibly chunked transfer coding. HTTP/2 carries typed binary frames:
Stream 1: HEADERS :method=GET :scheme=https :authority=example.test :path=/
Stream 3: HEADERS :method=GET :scheme=https :authority=example.test :path=/app.css
Stream 1: DATA [first bytes of HTML]
Stream 3: DATA [stylesheet bytes]
Stream 1: DATA [remaining HTML] END_STREAM
Expected result: frames from separate request-response streams can be interleaved on one connection without confusing one message’s content with another’s.
Every frame has an explicit type, length, flags, and stream identifier. The receiver no longer scans text for an empty line or chunk-size delimiters to find the next structural unit. Binary framing therefore improves processing regularity, but it does not make HTTP semantics “binary.” A method is still a method and a field still has defined semantics; only their serialization has changed.
Developer tools often display HTTP/2 as if it were HTTP/1.1 text because that notation is readable. The displayed lines are a reconstruction, not the octets sent on the wire.
Streams solved the pipelining order problem
Each HTTP/2 request-response exchange uses a stream. Frames carry stream identifiers, so a server can send part of one response, switch to another stream, and return later. One slow application response does not require every later response to wait behind it merely because the requests arrived first.
This is multiplexing rather than pipelining:
- HTTP/1.1 pipelining sends several requests but returns complete responses in request order.
- HTTP/2 multiplexing interleaves frames from independent streams.
Streams also have their own flow-control windows and lifecycle. A client can
cancel one request with RST_STREAM without necessarily destroying the entire
connection. Connection-level control remains available for settings, liveness,
flow control, and graceful shutdown.
The result is fewer connections for comparable concurrency. Fewer connections can use network capacity more cooperatively and preserve warm transport state for more exchanges.
Pseudo-fields carry message control data
HTTP/2 maps information from the HTTP/1.1 request line and status line into pseudo-fields:
:method: GET
:scheme: https
:authority: docs.example.test
:path: /history/
accept: text/html
Expected result: the receiver reconstructs the request method, target URI components, and ordinary field section without an HTTP/1.1 request line.
Response control data uses :status. Pseudo-fields must appear before regular
fields and have stricter placement rules than ordinary extension fields.
Connection-specific HTTP/1.1 fields such as Connection are not forwarded into
HTTP/2, because stream and connection control live in frames rather than those
field values.
These mapping rules let intermediaries translate between HTTP versions. A proxy can receive HTTP/2, construct an HTTP/1.1 request to the next hop, then translate the response back. The translation must preserve semantics while using valid framing for each connection.
HPACK compressed repetitive fields with shared state
Compressing field sections was harder than applying ordinary content compression. Many field names and values repeat across requests, so HTTP/2 gains efficiency from a table shared across the connection. HPACK uses:
- a static table of common names and values;
- a dynamic table updated during the connection;
- indexed references to table entries; and
- literal representations when a suitable entry is unavailable.
A later request can refer to an earlier field value using a compact index rather than sending every byte again. The table size is bounded and negotiated.
HTTP/2 did not reuse general-purpose DEFLATE compression for fields. Earlier compression of secret and attacker-controlled values in the same context had enabled attacks that inferred secrets from compressed size. HPACK was designed with explicit controls and representations for sensitive fields, though implementations still need to avoid indexing values such as authorization credentials when doing so would create risk.
Compression introduces statefulness at the connection layer. Both endpoints must apply table updates in a consistent order. HTTP/3 would need a different scheme, QPACK, because QUIC streams do not provide one total delivery order across the connection.
TLS and ALPN became the practical deployment path
HTTP/2 was defined for both encrypted and cleartext connections, but browser
deployment centered on HTTPS. During a TLS handshake, Application-Layer Protocol
Negotiation lets client and server agree on h2 before HTTP messages are sent.
That negotiation avoids spending an HTTP round trip asking an HTTPS server to upgrade. It also prevents the endpoints from interpreting the same connection as different protocols.
The original HTTP/2 specification defined an h2c cleartext upgrade path.
Deployment remained limited. RFC 9113 deprecated the HTTP/1.1 upgrade mechanism
for h2c; cleartext HTTP/2 now depends on prior knowledge rather than widespread
browser-style discovery.
The history illustrates a general rule: a mechanism can be valid in a specification and still fail to become a dependable deployment path. Operational support across clients, servers, proxies, and network policy matters as much as protocol correctness.
TCP still imposed connection-wide head-of-line blocking
HTTP/2 streams are independent at the application layer, but the entire connection runs over one ordered TCP byte stream. If a TCP segment is lost, later bytes cannot be delivered to HTTP/2 until the missing bytes are retransmitted, even when those later bytes contain frames for unrelated streams.
This is transport-layer head-of-line blocking. HTTP/2 removed the response-order blocking imposed by HTTP/1.1 pipelining, but packet loss could still pause every active stream sharing the TCP connection.
Using fewer TCP connections makes HTTP/2 more efficient under many conditions, yet it can concentrate loss impact. This unresolved constraint became one of the main motivations for carrying HTTP over a transport with independently delivered streams.
Priority and server push showed the cost of ambitious features
RFC 7540 defined a dependency tree and weights for stream priority. In theory, clients could describe which resources mattered most and servers could allocate bandwidth accordingly. In practice, implementations interpreted the tree differently, intermediaries changed signals, and maintaining priority state was complex. RFC 9113 deprecated the original scheme, while RFC 9218 defined a simpler extensible model based on urgency and incremental delivery.
Server push let a server send a response before the client explicitly requested
it. The server predicted that a resource would be needed and sent a
PUSH_PROMISE. The potential latency gain competed with several costs:
- the client might already have the resource in cache;
- the prediction might be wrong;
- pushed bytes could compete with more important responses;
- cache and intermediary behavior was complex; and
- application-level alternatives such as preload hints gave clients more control.
Server push remains in the protocol, but major browser use receded. Its history is not evidence that experimentation was a mistake. It is evidence that standardized capability does not guarantee net benefit under real deployment conditions.
HTTP/2 proved that wire versions and semantics can evolve separately
HTTP/2’s most durable architectural result is the clean separation it made visible. Applications did not need new URL schemes or method meanings to use a binary, multiplexed connection. Servers and frameworks could preserve their HTTP interfaces while protocol libraries changed the connection machinery.
The version substantially improved concurrency and field efficiency, but it also revealed where the transport limited further progress. Once many HTTP streams shared one TCP byte stream, the remaining loss coupling was impossible to solve entirely inside HTTP/2 framing. The next major transition therefore moved below HTTP as well as within it: QUIC would provide secure, independently delivered streams, and HTTP/3 would map HTTP semantics onto them.
Primary sources
- IETF announcement — HTTP/2 Approved (primary documentation)
- RFC 7540 — Hypertext Transfer Protocol Version 2 (rfc)
- RFC 7541 — HPACK Header Compression for HTTP/2 (rfc)
- RFC 7301 — TLS Application-Layer Protocol Negotiation (rfc)
- RFC 9113 — HTTP/2 (rfc)
- RFC 9218 — Extensible Prioritization Scheme for HTTP (rfc)
- Chrome removes HTTP/2 server push (primary documentation)
