Request Header Fields Too Large — what triggers it, how a runaway URL-encoding loop inflates a request past the limit, and the exact settings to change in Node.js, Nginx, and Apache. Plus the detail most guides get wrong: Nginx doesn’t actually return 431.
HTTP 431 — “Request Header Fields Too Large” — is defined in RFC 6585, Section 5. The server is refusing to process the request because the header block is too large: either one field exceeds the limit, or all the headers together do. The request is rejected at the protocol level, before your application code ever runs, so there’s no application log entry and often no response body — just a bare error page.
The two usual culprits are a very long URL/query string and accumulated cookies. This guide focuses on the first, because it’s the one URL encoding causes — and the one that’s easy to fix at the source.
431 was standardized in 2012 (RFC 6585). Nginx predates it and never adopted the code, so an oversized request on Nginx surfaces differently:
So if you’re chasing a 431 and your stack has Nginx in front, the symptom you’ll actually see is a 400 or 414 from Nginx — same root cause, different status code. Node.js (behind or without Nginx) is where the literal 431 comes from.
The encoding-specific cause is a double-encoding loop. A faceted filter or dashboard that captures the current, already-encoded query string and re-encodes the whole thing on each click turns every % into %25 on each pass:
color%3Ablue → first click
color%253Ablue → second click (%3A became %253A)
color%2525253Ablue → a few clicks later
Each pass roughly doubles the encoded length of every special character. Within a handful of interactions the URL crosses the server’s header-size limit and the request is refused. (If you’ve got a runaway value now, paste it into our decoder with “Decode recursively” to peel every layer off and see the original.)
The fix is to never encode a string that’s already in the URL. Keep filter state as plain values in memory and rebuild the query string from scratch each time:
// Wrong — re-encodes the already-encoded URL each time
url = encodeURIComponent(location.search + newFilter);
// Right — build fresh from unencoded state, encode once
const params = new URLSearchParams();
for (const [key, value] of Object.entries(state)) {
params.set(key, value); // URLSearchParams encodes exactly once
}
history.replaceState(null, '', '?' + params.toString());
Fix the encoding loop first — raising a limit to accommodate a value that grows without bound only postpones the failure. Once the source is clean, if legitimate requests are still too large, adjust the server limit.
Node’s default maximum header size is 16 KB. Raise it with the CLI flag or the server option:
# Check the current limit
node -e "console.log(require('http').maxHeaderSize)"
# Start with a larger limit
node --max-http-header-size=32768 app.js
Or set it when creating the server:
const http = require('http');
const server = http.createServer({ maxHeaderSize: 32768 }, handler);
Nginx controls header buffers with large_client_header_buffers (default 4 8k — four 8 KB buffers) and the initial client_header_buffer_size (default 1k). Raise them in the http or server block:
http {
client_header_buffer_size 4k;
large_client_header_buffers 4 16k;
}
A request line cannot exceed the size of a single buffer, so the buffer size (the second number) is what caps a long URL, not the count.
Apache caps each header field with LimitRequestFieldSize (default 8,190 bytes) and the field count with LimitRequestFields (default 100):
LimitRequestFieldSize 16384
LimitRequestFields 100
Bigger header buffers mean more memory allocated per connection, and above roughly 32 KB you start trading a functional problem for a denial-of-service one — an attacker can force large allocations across many connections. Raise limits only as far as real traffic needs, and pair the change with fixing whatever inflated the request in the first place. For a URL-encoding loop, that fix is in your code, not your server config.
large_client_header_buffers and client_header_buffer_size directives and their defaults. nginx.org/en/docs--max-http-header-size flag and maxHeaderSize option. nodejs.org/apiHonest scope: default limits and status-code behaviour change between server versions — the values here reflect current defaults (Node 12+ at 16 KB, Nginx 4 8k, Apache 8,190 bytes). Check your own server’s version before relying on a specific number.
Not usually. Nginx predates RFC 6585 (which defines 431) and instead returns 414 (Request-URI Too Large) when the request line — the URL — is too long, or 400 (Bad Request) when an individual header field is too large. If you see “400 Bad Request: Request Header Or Cookie Too Large” from Nginx, that’s the same underlying problem a 431 describes elsewhere.
It varies by server. Node.js defaults to a 16 KB maximum header size (raised from 8 KB in Node 12). Nginx uses large_client_header_buffers with a default of four 8 KB buffers. Apache defaults LimitRequestFieldSize to 8,190 bytes per header field.
Indirectly, through inflation. A UI that re-encodes an already-encoded query string on each interaction turns every % into %25, so a value grows %3A → %253A → %25253A and keeps ballooning until the request exceeds the header-size limit. Fixing the double-encoding loop is the real fix; raising the limit only delays it.
Raise it only enough to fit legitimate traffic, and fix the root cause too. Large limits (above ~32 KB) increase denial-of-service exposure, since an attacker can force the server to allocate big buffers per connection. If you’re hitting the limit from a runaway query string or oversized cookies, address that first.
Either. The limit applies to the whole header block, so the two most common causes are a very long URL/query string (often from an encoding loop) and accumulated cookies — session tokens, analytics IDs, and A/B flags concatenated into one Cookie header. Check both in DevTools → Network on the failing request.
Written and maintained by the urlencodedecode.com team. Every technical claim on this page is verified against primary sources — the RFCs (3986, 3629, 4648, 7578), the WHATWG URL Standard, and official vendor or language documentation — rather than second-hand summaries. When a source contradicts a common assumption, we follow the source and note the discrepancy. Corrections: contactus@urlencodedecode.com.