431 Request Header Fields Too Large
Client error response defined by RFC 6585 §5.
Last updated July 27, 2026
Common causes at a glance
- Cookie header bloated past the server buffer by accumulated cookies
- An oversized Authorization header, typically a JWT stuffed with claims
- A long Referer or a URL-encoded state parameter echoed into a header
- Many small custom X- headers injected by a proxy or tracing layer
Reproduce it
curl -sSi -o /dev/null -w '%{http_code}\n' -H "X-Big: $(printf 'a%.0s' {1..20000})" https://www.example.com/What 431 Request Header Fields Too Large means
Servers allocate a fixed buffer for request headers before they know how big the request is, so there has to be a ceiling. Defaults are small: nginx allows 8 KB per header line, and cloud load balancers cap the whole block in the tens of kilobytes.
Cookies dominate because they accumulate. Every cookie set on a domain is sent on every subsequent request to it, so a few analytics tools, a session, a CSRF token and some feature flags compound until one more login pushes the request over the edge. The failure looks bizarre: one user cannot load the site at all, from any page, while everyone else is fine, and clearing cookies fixes it.
What the spec says
RFC 6585 §5 defines 431 and covers both cases: the header
section as a whole being too large, and a single field being too large. It says the
response ought to indicate which field was at fault, which almost no implementation does,
and the code is not cacheable. Worth knowing: the spec permits a server to close the
connection instead, and many do — nginx typically returns 400 with
client_header_too_large in its error log, and some load balancers reset with
no status at all. HTTP sets no length limit of its own;
RFC 9110 §5.4 tells implementations to pick one.
What actually causes it
Cookies first. A JWT session cookie carrying group memberships grows with the user’s permissions, so the accounts that break are the admins with the most roles. SAML and OIDC flows are another reliable source, since some libraries split a large assertion across numbered cookie chunks that all get sent together. Then there is the proxy tax: service meshes and tracing systems add headers on the way in, so a request comfortably under the limit at the edge can exceed it at the origin. The limit applies to the request, so the answer is never on the response side.
How to debug it
Measure before you theorise. Sum what you are actually sending:
curl -sS -o /dev/null -v https://www.example.com/ 2>&1 \
| awk '/^> /{n += length($0) - 2} END {print n " bytes of request headers"}'
Then bisect. Repeat with -H 'Cookie:' to send an empty cookie header; if it
succeeds, cookies are the problem and you can halve the set until you find the offender.
An incognito window is the fastest confirmation that stored state is the cause.
Raise the server ceiling only as a stopgap: large_client_header_buffers in
nginx, LimitRequestFieldSize in Apache. The durable fix is to send less —
move session state server-side behind an opaque identifier and keep claims out of JWTs
that ride on every request.
Working examples
curl
# How many bytes of headers is this request actually sending?
curl -sS -o /dev/null -v https://www.example.com/ 2>&1 \
| awk '/^> /{n += length($0) - 2} END {print n " bytes"}'
# Bisect: does it work with no cookies at all?
curl -sSi -o /dev/null -w '%{http_code}\n' -H 'Cookie:' https://www.example.com/
# Reproduce deliberately with an oversized header.
curl -sSi -H "X-Big: $(printf 'a%.0s' {1..20000})" https://www.example.com/
Python (requests)
import requests
big = "a" * 20000
resp = requests.get("https://www.example.com/", headers={"X-Big": big}, timeout=10)
print(resp.status_code) # 431, or 400 depending on the server
# Measure a real session's cookie weight before it becomes a production incident.
s = requests.Session()
s.get("https://www.example.com/login", timeout=10)
size = sum(len(c.name) + len(c.value) + 3 for c in s.cookies)
print(f"cookie header would be ~{size} bytes")
Node (fetch)
const res = await fetch("https://www.example.com/", {
headers: { "X-Big": "a".repeat(20000) },
});
console.log(res.status); // 431, or a connection reset on some load balancers
// Audit what you are about to send rather than waiting for the failure.
const headers = new Headers({ Cookie: "session=abc; _ga=xyz" });
let bytes = 0;
for (const [k, v] of headers) bytes += k.length + v.length + 4;
console.log(`request headers: ${bytes} bytes`);Reference and tooling
The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.
Other 4xx codes
- 400 Bad Request
- 401 Unauthorized
- 402 Payment Required
- 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 406 Not Acceptable
- 407 Proxy Authentication Required
- 408 Request Timeout
- 409 Conflict
- 410 Gone
- 411 Length Required
- 412 Precondition Failed
- 413 Content Too Large
- 414 URI Too Long
- 415 Unsupported Media Type
- 416 Range Not Satisfiable
- 417 Expectation Failed
- 418 (Unused)
- 419 Page Expired
- 421 Misdirected Request
- 422 Unprocessable Content
- 423 Locked
- 424 Failed Dependency
- 425 Too Early
- 426 Upgrade Required
- 428 Precondition Required
- 429 Too Many Requests
- 444 No Response
- 451 Unavailable For Legal Reasons
- 494 Request Header Too Large
- 495 SSL Certificate Error
- 496 SSL Certificate Required
- 497 HTTP Request Sent to HTTPS Port
- 499 Client Closed Request