nginx/nginx
HTTP/3
Active contributors: Roman Arutyunyan, Sergey Kandaurov
Purpose
HTTP/3 (RFC 9114) running over QUIC. The HTTP/3 layer parses HTTP/3 frames, manages QPACK header compression, and bridges QUIC streams into the same ngx_http_request_t objects used by HTTP/1 and HTTP/2. The QUIC transport itself is in src/event/quic/ (see quic).
First mainline commit: 2020-03-13. The current implementation became non-experimental in the 1.25.x mainline.
Directory layout
src/http/v3/
├── ngx_http_v3.{c,h} # module init + ngx_http_v3_module bootstrap
├── ngx_http_v3_module.c # http3_* directives
├── ngx_http_v3_request.c # the QUIC stream → ngx_http_request_t bridge
├── ngx_http_v3_filter_module.c # output filter producing HTTP/3 frames
├── ngx_http_v3_parse.{c,h} # frame + setting parsers
├── ngx_http_v3_encode.{c,h} # field-line encoding for HEADERS frames
├── ngx_http_v3_table.{c,h} # the QPACK dynamic table
└── ngx_http_v3_uni.{c,h} # unidirectional control / encoder / decoder streamsKey abstractions
| Type / function | Role |
|---|---|
ngx_http_v3_session_t |
Per-QUIC-connection HTTP/3 state (settings, QPACK tables, control streams) |
ngx_http_v3_parse_t |
Frame parser state (one per request stream) |
ngx_http_v3_init_request_stream() |
Called when a new bidirectional stream opens — bootstraps the request bridge |
ngx_http_v3_init_uni_stream() |
Called for unidirectional control / encoder / decoder streams |
ngx_http_v3_send_settings() |
Open the control stream, send SETTINGS |
Stream model
QUIC has two stream flavors:
- Bidirectional, client-initiated — one HTTP request/response pair per stream
- Unidirectional — one of three reserved purposes:
- Control stream (type 0x00) — SETTINGS, GOAWAY, MAX_PUSH_ID
- QPACK encoder stream (type 0x02) — instructions to mutate the dynamic table
- QPACK decoder stream (type 0x03) — header-block ack / cancellation
ngx_http_v3_uni.c reads the first byte of each unidirectional stream to determine type, then routes.
Frame types
| Frame | Direction | Purpose |
|---|---|---|
DATA |
both | Request / response body |
HEADERS |
both | Header block (QPACK-encoded) |
CANCEL_PUSH |
both | Cancel a server push |
SETTINGS |
control stream | Connection settings |
PUSH_PROMISE |
server → client | (Unused — push not implemented client-side) |
GOAWAY |
both | Begin connection shutdown |
MAX_PUSH_ID |
client → server | Push limit |
Request flow
sequenceDiagram
participant QC as QUIC connection
participant HS as ngx_http_v3_session_t
participant SP as ngx_http_v3_parse
participant RP as ngx_http_request_t
participant Engine as Phase engine
QC->>HS: stream opened (bidi, client-initiated)
HS->>SP: init parse state for stream
QC-->>SP: data chunks (frames + bytes)
SP->>SP: parse HEADERS frame
SP->>RP: ngx_http_v3_request_create + populate
RP->>Engine: ngx_http_process_request
Engine->>Engine: 11 phases as usual
Engine->>RP: response headers + body
RP->>SP: output via ngx_http_v3_filter
SP-->>QC: frames written to streamQPACK
QPACK (RFC 9204) is HTTP/3's header compression. Like HPACK it has a static table and a dynamic table, but unlike HPACK the dynamic table is shared across streams via a separate encoder stream that carries table-mutation instructions out-of-band. This decouples header block ordering from table updates so that head-of-line blocking is avoided.
src/http/v3/ngx_http_v3_table.c implements the dynamic table on both encode and decode sides. Inserts on the encoder stream are processed in arrival order; references in HEADERS blocks are resolved against a snapshot of the table.
Settings
ngx_http_v3_session_t tracks the negotiated values of:
SETTINGS_MAX_FIELD_SECTION_SIZESETTINGS_QPACK_MAX_TABLE_CAPACITYSETTINGS_QPACK_BLOCKED_STREAMS
Defaults match the spec; nginx tunables exposed via http3_max_concurrent_streams, http3_max_field_size, http3_max_table_capacity.
Output
The HTTP/3 filter (ngx_http_v3_filter_module) replaces the chunked / write filter for H3 streams. It encodes response headers as a HEADERS frame (with QPACK), wraps body buffers in DATA frames, and pushes them onto the QUIC stream's send queue.
ALPN
H3 uses ALPN identifier h3. When a QUIC connection completes the TLS handshake with h3 selected, ngx_http_v3_init is the entry point. The HTTP/2 / HTTP/1.1 paths are not relevant here — H3 always runs over QUIC.
Integration points
- QUIC transport (
src/event/quic/) — the source of stream events. H3 doesn't read fromc->fd; it reads from QUIC stream APIs. - HTTP request engine — the same 11 phases, filters, content handlers.
- TLS / OpenSSL — handled by the QUIC layer using OpenSSL's keying APIs (and the OpenSSL compat shim when the linked OpenSSL doesn't expose them natively).
Alt-Svcheader —quic_max_idle_timeout,quic_active_connection_id_limit, etc., advertised to clients throughAlt-Svcfrom the HTTP/1.1 path.
Entry points for modification
The "narrow waist" between QUIC and HTTP is ngx_http_v3_request.c. If a feature requires changes in the request ↔ stream bridge (e.g., trailers, QUIC-specific abort semantics), this is the file. QPACK changes go in ngx_http_v3_table.c. SETTINGS or new frame types — ngx_http_v3_parse.c. The QUIC layer handles everything below the frame boundary.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.