A new guide documents a misconfiguration on this resolver and the five weeks it went unnoticed. The directive http2_body_preread_size had been lowered to 16k as an anti-DDoS measure, on reasoning that holds up well on paper: nginx allocates that buffer in full for every HTTP/2 stream carrying a body, so a smaller value cuts the worst-case memory an attacker can force you to allocate.
The directive also doubles as the HTTP/2 INITIAL_WINDOW_SIZE the server advertises. Until a client acknowledges that SETTINGS frame it is still entitled to assume the protocol default window of 65535 bytes, so it may legally send more than a smaller buffer can hold. Rather than risk the overflow, nginx refuses the stream with RST_STREAM(REFUSED_STREAM). Below 65535 that branch is permanently reachable, and every POST over HTTP/2 fails - on every path, including ones that do not exist, because the refusal happens before routing.
It stayed invisible for a specific and generalizable set of reasons. The refusal logs at INFO, which a production error log discards. GET requests were unaffected, so the site looked healthy. HTTP/1.1 and HTTP/3 were unaffected, so clients with fallback logic succeeded quietly. nginx -t passed. And RFC 9113 defines REFUSED_STREAM to mean retrying is safe, so affected clients retried in silence instead of surfacing an error.
An eight-minute window of INFO logging recorded 211 refused streams from 10 distinct client addresses, one of them retrying 115 times - roughly 1,590 an hour, from legitimate users. Raising the value to 65535 cost about 2.5 MB of resident memory, or 3%. The guide includes the code path, the one-byte boundary test, and a 60-second reproduction on localhost that needs nothing but nginx, openssl and curl.