HTTP/2
Text-based messaging and head-of-line blocking in HTTP/1.1 limited how efficiently browsers loaded modern web pages. HTTP/2 addresses this with a binary framing layer enabling multiplexed streams, header compression, and more efficient use of TCP connections. Published in May 2015 as RFC 7540 and revised in June 2022 as RFC 9113, the protocol preserves full semantic compatibility with HTTP/1.1.
All HTTP methods, status codes, and header fields remain the same. The changes are in how data is framed and transported, not in what the data means.
SPDY origins
Google announced the SPDY ("speedy") protocol in 2009 as an experiment to reduce web page load latency. SPDY introduced binary framing, multiplexing, header compression, and server push over a single TLS connection.
After SPDY demonstrated measurable improvements and gained adoption in Firefox and Opera, the IETF selected SPDY as the starting point for HTTP/2. The initial HTTP/2 draft was based directly on SPDY. SPDY was deprecated after HTTP/2 was ratified, and browser support was removed in 2016.
Binary framing
HTTP/2 encodes all communication as binary frames. Each frame has a 9-byte header containing the length, type, flags, and a 31-bit stream identifier. Frames are the smallest unit of communication.
Three concepts define the transport model:
- Frame: a single unit of data identified by stream ID and type
- Message: a complete sequence of frames mapping to an HTTP request or response
- Stream: an independent, bidirectional sequence of frames within a connection
All streams share a single TCP connection. Frames from different streams are interleaved and reassembled at the receiving end based on stream identifiers.
Frame types
| Frame | Type | Purpose |
|---|---|---|
| DATA | 0x00 | Carries request or response body |
| HEADERS | 0x01 | Opens a stream, carries header fields |
| PRIORITY | 0x02 | Stream priority (deprecated) |
| RST_STREAM | 0x03 | Terminates a single stream |
| SETTINGS | 0x04 | Connection configuration |
| PUSH_PROMISE | 0x05 | Server push notification |
| PING | 0x06 | Round-trip time measurement |
| GOAWAY | 0x07 | Graceful connection shutdown |
| WINDOW_UPDATE | 0x08 | Flow control adjustment |
| CONTINUATION | 0x09 | Continues a HEADERS block |
RST_STREAM allows a client to cancel a specific stream (for example, when moving away from a page) without affecting other streams or closing the connection.
The table covers the core protocol. Extensions registered three more frame types: ALTSVC (0x0a) carries Alt-Svc information at the frame level, ORIGIN (0x0c) lists the origins a connection serves, and PRIORITY_UPDATE (0x10) reprioritizes a response already in flight under the extensible prioritization scheme.
Multiplexing
Multiplexing allows multiple HTTP requests and responses to be in flight simultaneously on a single TCP connection. This addresses the head-of-line blocking problem from HTTP/1.1, where responses had to arrive in order on each connection.
With HTTP/1.1, browsers used up to six parallel TCP connections per origin to achieve concurrency. With HTTP/2, a single connection handles all requests concurrently through interleaved streams. This reduces TCP handshake overhead, improves congestion window utilization, and lowers server resource consumption.
HPACK header compression
HTTP/2 compresses Headers using HPACK (RFC 7541). HPACK uses three mechanisms:
- Static table: 61 predefined entries for
commonly used header name-value pairs (
:method: GET,:scheme: https,accept-encoding: gzip, deflate) - Dynamic table: a FIFO table storing recently encoded headers, shared between encoder and decoder
- Huffman encoding: variable-length binary codes compress string literals (5 to 30 bits per character)
HPACK was designed to resist the CRIME attack, which exploited character-by-character guessing in DEFLATE compression. HPACK only reveals matches on complete header values and supports never-indexed literals for sensitive fields like Cookies and authorization tokens.
Pseudo-header fields
HTTP/2 replaces the HTTP/1.1 request line
and status line with pseudo-header fields, prefixed
with : (colon). These are not regular HTTP headers
and must appear before all regular header fields.
| Field | Purpose |
|---|---|
:method |
HTTP method |
:scheme |
URI scheme (http or https) |
:authority |
Host and port (replaces Host) |
:path |
Path and query string |
:status |
HTTP status code (response only) |
Encoding these as header fields allows them to benefit from HPACK compression.
Flow control
HTTP/2 implements credit-based flow control at both the stream and connection level using WINDOW_UPDATE frames. Each new stream starts with a 65,535-byte window. Only DATA frames are subject to flow control. Control frames are always delivered.
SETTINGS parameters
The SETTINGS frame carries six defined parameters, and the defaults shape every connection before the first exchange completes:
| Parameter | Initial value |
|---|---|
| SETTINGS_HEADER_TABLE_SIZE | 4,096 octets |
| SETTINGS_ENABLE_PUSH | 1 (enabled) |
| SETTINGS_MAX_CONCURRENT_STREAMS | No limit, with at least 100 recommended |
| SETTINGS_INITIAL_WINDOW_SIZE | 65,535 octets |
| SETTINGS_MAX_FRAME_SIZE | 16,384 octets, raisable to 16,777,215 |
| SETTINGS_MAX_HEADER_LIST_SIZE | Unlimited (advisory) |
Tuning starts here: a server capping concurrent streams too low serializes a browser's parallel requests, and a larger initial window lifts per-stream throughput on high-latency paths before WINDOW_UPDATE frames catch up.
Stream prioritization
RFC 7540 defined a priority system using dependency trees and stream weights. In practice, implementations varied widely, and many servers ignored client priority signals entirely.
RFC 9113 deprecated the original scheme. RFC 9218 (Extensible Prioritization Scheme for HTTP) introduced a simpler Priority header field working across both HTTP/2 and HTTP/3, plus the PRIORITY_UPDATE frame for changing the priority of a response after the request has gone out, the one case a header field cannot cover.
Server push
Server push allowed a server to proactively send responses before the client requested them, using PUSH_PROMISE frames. The intent was to eliminate round trips for resources the server anticipated the client needed.
ALPN negotiation
HTTP/2 is negotiated during the TLS handshake via the Application-Layer Protocol Negotiation (ALPN) extension. The client lists supported protocols in the ClientHello. The server selects one and returns the choice in the EncryptedExtensions message in TLS 1.3, or in the ServerHello in TLS 1.2.
- h2: HTTP/2 over TLS, the identifier sent in ALPN
- h2c: HTTP/2 over cleartext TCP, reserved from the ALPN identifier space but never sent in ALPN. It was negotiated through the HTTP/1.1 Upgrade mechanism, which RFC 9113 deprecated.
Cleartext HTTP/2 still runs where the client already
knows the server speaks it, the prior-knowledge mode,
skipping negotiation entirely. gRPC between services
and nginx's http2 on; directive on a cleartext
listener rely on it, so cleartext
HTTP/2 remains in use even though the Upgrade path is
gone.
All major browsers only support HTTP/2 over TLS.
The connection begins with a 24-byte client preface
(PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n) followed by a
SETTINGS frame. This sequence was designed to cause
HTTP/1.1 servers to reject the connection
cleanly.
Network layers
HTTP/1.1 operates as two or three layers depending on whether TLS is present: HTTP over TCP, or HTTP over TLS over TCP. HTTP/2 clarified this by effectively requiring TLS, creating a consistent three-layer stack: HTTP/2 over TLS over TCP. All three layers remain independent. The binary framing sits above TLS, which sits above TCP.
HTTP/3 breaks from this model by integrating TLS directly into the QUIC transport, collapsing the three layers into two. This tighter integration is what enables the connection setup improvements and encryption of transport-level headers.
Security
HTTP/2 effectively requires TLS in practice. RFC 9113 Section 9.2 mandates TLS 1.2 at minimum, prohibits TLS compression (CRIME mitigation), prohibits TLS renegotiation, and requires ephemeral key exchange. A list of prohibited cipher suites includes many common TLS 1.2 defaults.
HTTP/2 Rapid Reset (CVE-2023-44487)
This vulnerability exploits multiplexing and RST_STREAM: an attacker rapidly opens and immediately cancels streams, causing significant server-side overhead with minimal client cost. The attack produced record-breaking DDoS volumes. Mitigations include rate-limiting stream creation and reset frequency.
CONTINUATION flood (VU#421644)
A header block ends only when a frame arrives with the END_HEADERS flag set. Implementations failing to cap the frames in a single block let an attacker stream CONTINUATION frames indefinitely, never setting the flag, exhausting memory or burning CPU in HPACK decoding. The 2024 advisory hit Node.js, Go's net/http, Apache httpd, and Envoy among others, and the fix mirrors Rapid Reset: bound what a peer sends before the message completes.
Adoption
HTTP/2 adoption was rapid because existing websites and server applications required no changes. The protocol is transparent to application code. All major browsers support HTTP/2 over TLS.
Despite technical allowance for cleartext HTTP/2, browser vendors collectively chose to support HTTP/2 only over HTTPS. This decision raised the security floor for the entire web. Deploying HTTP/2 meant deploying TLS.
Testing HTTP/2
Browser DevTools
Open the Network tab, right-click the column headers,
and enable Protocol. HTTP/2 connections show
as h2.
curl
curl --http2 -sS -o /dev/null -D- https://example.re
The response status line shows HTTP/2 when the
server supports HTTP/2. Use -v (verbose) to see
ALPN negotiation during the TLS handshake.
Online tools
nghttp
The nghttp command-line client (part of nghttp2)
provides detailed HTTP/2 frame-level output:
nghttp -nv https://example.re
This shows the full frame exchange including SETTINGS, HEADERS, and DATA frames, useful for debugging HTTP/2 behavior at the protocol level.
See also
- RFC 9113: HTTP/2
- RFC 7541: HPACK
- RFC 9218: Extensible Prioritization
- HTTP/0.9
- HTTP/1.0
- HTTP/1.1
- HTTP/3
- ALPN
- HTTP Explained