Commit Graph
3 Commits
Author SHA1 Message Date
Viktor Liu 236bbfc7ee Restrict the HTTP/1.1 fallback retry and tighten the h2 failure classification 2026-09-04 07:14:25 +02:00
Viktor Liu 603e163d42 Keep the HTTP/1.1 pin for an upstream that asked for it
HTTP_1_1_REQUIRED is not a fault: it is the upstream describing its own
configuration. IIS answers it for sites using Windows Authentication —
connection-oriented auth that h2 multiplexing cannot carry — and for
client-certificate sites, where the per-request certificate needs a TLS
renegotiation h2 forbids. Browsers retry those on HTTP/1.1 silently,
which is why such a site works in a browser and fails behind this proxy.

Expiring that pin every 10 minutes would buy nothing but one failed
request per interval, since nothing about the upstream has changed. So a
pin the upstream asked for holds until the transport goes away with the
proxy or the account's client, and the bounded TTL stays for the
ambiguous protocol errors, where the failure may well clear on its own.
A later ambiguous failure never weakens a permanent pin.
2026-09-03 18:18:00 +02:00
Viktor Liu 582c8b1ae8 Fall back to HTTP/1.1 when an upstream cannot serve the h2 it negotiated
ForceAttemptHTTP2 only puts h2 in the ALPN offer — the upstream still
picks — so "auto" already meant "whatever the upstream chose". What ALPN
cannot express is an upstream that selects h2 and then fails to speak it,
which is the case the setting was added for: today that leaves the
operator pinning every upstream to 1.1 to work around one broken backend.

Auto now completes itself. The first h2-level failure for a host pins
that host to an HTTP/1.1-only clone of its transport for 10 minutes and
retries the request there when the body can be replayed, so a broken
backend costs one failed h2 attempt instead of a configuration change.
The pin is per upstream host, so one broken backend does not drop the
others, and it expires so a fixed backend returns to h2 on its own.

Only h2 framing errors trigger it: a dial, TLS or context error says
nothing about the protocol and retrying it over HTTP/1.1 would fix
nothing. The explicit values stay absolute — "2" never downgrades.

Pinning HTTP/1.1 now also strips h2 from the ALPN offer. Configuring h2
makes net/http append it to the transport's TLSClientConfig, so a clone
taken from a transport that already served a request would otherwise
advertise a protocol the clone refuses to speak, and the reply would come
back as h2 frames parsed as an HTTP/1.1 message.
2026-09-03 18:18:00 +02:00