HTTP/2
http_protocols is an ingress allowlist, default [http1, http2]. It must be a
nonempty list without duplicates; select [http1] or [http2] to disable the
other protocol. The old http2.prior_knowledge option is no longer accepted;
replace prior_knowledge: true with target-level http_protocols: [http2].
With both protocols enabled, plaintext connections are detected incrementally:
PRI selects H2; the first mismatch selects H1. All consumed bytes, including
any read-ahead from optional TLS detection, are replayed unchanged. The H2 library
validates the remaining preface. Invalid prefaces never fall back to H1. This
reserves PRI as an H2-only method on mixed plaintext listeners, while longer H1
methods such as PRINT still work. Detection runs only once per connection, not
for each keepalive request. Single-protocol listeners skip detection entirely.
Detection uses one absolute deadline from entry into HTTP dispatch, including
waiting for the first byte: http_timeouts.request_header_timeout_secs when set,
otherwise http2.header_timeout_secs (10 seconds) when omitted. Explicit null
disables that frontend timer. Byte progress does not extend it. After selection,
the existing handler’s header/handshake deadlines apply.
TLS never sniffs HTTP bytes. Negotiated h2 selects H2; http/1.1, custom ALPN
names, and no ALPN select H1, provided that protocol is enabled. Omitted TLS ALPN
is derived as described in incoming TLS;
explicit ALPN is authoritative. H2-only TLS
requires h2 negotiation. Generic TLS target matching still precedes HTTP
dispatch. Raw streams and TLS passthrough are unaffected. Upgrade-based h2c is
not supported.
A forward action independently selects upstream_protocol: http1 | http2
(default http1). H2 egress uses prior knowledge over clear TCP/Unix sockets or
requires negotiated h2 over TLS. Omit client ALPN to derive [h2], or configure
exactly [h2]; incompatible explicit lists are rejected. No automatic fallback.
target: allowlist: 127.0.0.1/32 http_protocols: [http1, http2] http2: max_concurrent_streams: 64 max_connections: 256 max_backend_connections: 256 max_header_list_size: 65536 header_timeout_secs: 10 connect_timeout_secs: 10 body_progress_timeout_secs: 30 drain_timeout_secs: 5 default_http_action: type: forward upstream_protocol: http2 location: address: backend.example:443 client_tls: trueProtocol policy and limit values shown are defaults. Enabling H2 validates local
response statuses and headers for H2 compatibility, even when an explicit ALPN
list restricts TLS to H1. Use [http1] for H1-only local response configurations.
max_concurrent_streams is per ingress H2 connection (maximum 4096).
max_connections bounds admitted H2 connections per compiled target/generation,
after TLS negotiation. max_backend_connections initializes two separate
target-global limits: active new-path exchanges and live new-path backend
transports, including dialing, idle and draining connections. Both connection
limits are capped at 65536. Each frontend additionally retains at most 16 H2
backend drivers across all actions. Admission refuses excess work rather than
creating an unbounded queue. H1-to-H1 connections retain their existing policy.
Header limits are 1024 through 1048576 decoded bytes, with a fixed 128-field cap.
Receive windows are 64 KiB per stream and 1 MiB per connection; forwarding chunks
are at most 16 KiB. Up to 16 informational responses are forwarded. Encoded
field-section bytes and pre-consumption response-section counts are also bounded.
Trailer fields are validated independently of initial headers and header patches.
Pseudoheaders and oversized trailer sections are rejected before conversion to
ordinary fields, for both ingress requests and upstream H2 responses, using the
pinned h2 fork fixes. Literal # fragments are rejected
before URI normalization on both H2 ingress and H1-to-H2 forwarding, so the backend
cannot receive a different path from the one used for routing. Encoded %23
remains permitted. These checks require no additional configuration flags.
For sections received over HTTP/2, max_header_list_size is an inclusive receive
policy, counting each field’s name, value, and 32-byte overhead. Valid trailer
sections exactly at the limit are preserved in full; sections one byte over it
are rejected, not truncated. HTTP/1 fields on translation paths retain their
existing accounting: raw field-line length, including the colon and whitespace,
plus 32 bytes. The configured policy is distinct from the H2 decoder’s additional
abuse limits, which can terminate the connection rather than only the stream.
All H2 timeouts must be positive and at most 86400 seconds. The connect deadline
includes action-slot waiting, DNS/TCP/Unix setup, TLS and H2 handshake/readiness.
The header deadline is absolute for incomplete frames/header blocks, not extended
by trickling bytes; expiry closes the connection. Existing explicit request-header
timeout overrides the ingress frame/header default. Generic pre-ALPN TLS handshake
policy remains controlled by server_tls.handshake_timeout_secs.
Response-header and upstream-body progress deadlines are disabled by default.
An explicit response-header deadline is absolute from request-head submission,
including concurrent upload and informational responses. An explicit upstream-body
progress deadline bounds stalled upstream I/O, not downstream backpressure or
waiting for frontend upload bytes. http2.body_progress_timeout_secs still
protects frontend upload reads and downstream writes. See the complete
timeout contract.
Frontend idle means no active streams, default 60 seconds, overridden or disabled
with http_timeouts.keepalive_idle_timeout_secs. Reload/idle retirement sends
GOAWAY and bounds each owner’s drain; top-level reload does not synchronously
join established TCP sessions.
H2 preserves the logical request scheme and authority independently of backend TLS/SNI. Host patches update outgoing authority. Repeated headers, DATA and trailers are preserved; H1 ingress retains its existing UTF-8 restriction. Cross-version translation rejects unsupported transfer-coding chains and framing-changing patches, and uses H1 chunking when trailers may follow. H2-to-H1 backend leases are exclusive and not cached. H2 backend reuse is frontend/action-scoped, without cross-action coalescing or automatic retries; round-robin advances per physical connection, not per stream.
H2-to-H1 requests use one canonical Host, Connection: close, no incoming
Content-Length or TE field, and chunked framing whenever the upload is open.
DATA bytes cannot supply their own chunk boundaries. Failed uploads are not
completed with a synthesized terminal chunk, and their backend sockets are never
reused. If an early response is already in progress, upload failure half-closes
the backend write side so an EOF-dependent response can finish.
CONNECT, extended CONNECT/WebSocket, H3 and push are not supported on H2 paths.
H1 upgrades directed to an H2-only action receive 501. Local H2 responses must be
final statuses; HEAD/204/205/304 do not transmit payload. Custom reason phrases
are H1-only. close intentionally closes the entire H2 connection and its siblings;
ordinary local responses end only their stream.