A 431 means the server refused to process your request because the HTTP headers are too large. Either a single header exceeds the server’s limit, or the combined size of all headers together is too much. The request never reaches the application – the server rejects it at the protocol level before parsing the body.
This is almost always caused by accumulated cookies, oversized JWT tokens, or a flood of custom headers added by proxies, extensions, or middleware. The server is protecting itself from requests that consume too much memory to parse.
Quick reference#
| Symptom | Likely cause | Fix |
|---|---|---|
| 431 in browser on one specific site | Cookies accumulated over time | Clear cookies for that domain |
| 431 in browser on all sites | Browser extension injecting large headers | Disable extensions and test in incognito |
| 431 on API calls with Bearer tokens | JWT token too large (too many claims) | Reduce token payload or switch to opaque tokens with server-side sessions |
| 431 after deploying a reverse proxy | Proxy adding forwarded headers on each hop | Fix duplicate header appending in proxy chain |
| “bad message 431” in Java/Jetty logs | Jetty’s default header limit (8KB) exceeded | Increase
requestHeaderSize
in Jetty config |
| 431 on Node.js server | Node’s default header limit (16KB) exceeded | Start with
--max-http-header-size
flag |
| 431 intermittent, only some users | Those users have more cookies than others | Audit cookie size per user segment |
What the HTTP specification says#
HTTP 431 is defined in RFC 6585, Section 5. The specification says the server is unwilling to process the request because its header fields are too large. The request may be resubmitted after reducing the size of the request header fields.
The specification distinguishes two cases:
- An individual header field is too large (for example, a single
Cookieheader that is 32KB) - The total set of header fields is too large (many headers that individually are fine but collectively exceed the limit)
The server should indicate which case applies in the response body, though most implementations do not.
HTTP/1.1 431 Request Header Fields Too Large
Content-Type: text/html
Connection: close
A 431 is different from a 400 Bad Request. A 400 means the request is malformed – bad syntax, invalid encoding, or broken protocol. A 431 means the request syntax is fine but the headers are too large for the server to handle. Some servers (notably Nginx) return 400 instead of 431 for oversized headers because Nginx predates RFC 6585. If you see 400 Bad Request: Request Header Or Cookie Too Large in Nginx, it is the same underlying issue.
The two limit types#
Single header too large
One header exceeds the server’s per-header buffer. The most common offender is the
Cookie
header, which contains every cookie set for the domain concatenated into a single value:
Cookie: session_id=abc123; analytics_1=...; analytics_2=...; tracking=...;
preferences=...; ab_test_1=...; ab_test_2=...; (continues for 16KB)
Every analytics tool, A/B testing platform, chat widget, and marketing pixel that sets a cookie adds to this header. On a site with 15 third-party scripts, each setting 2-3 cookies, the
Cookie
header can easily reach 8-16KB.
Total headers too large
No single header is oversized, but the sum of all headers exceeds the server’s total limit. This happens with:
- Reverse proxy chains that append
X-Forwarded-For,X-Forwarded-Proto,X-Request-ID, and custom headers at each hop - API gateways that inject authentication, rate limit, and routing headers
- Browser extensions that add custom headers to every request
- Application frameworks that set many small headers (CORS, security, caching)
Common causes#
Cookies accumulating over time
The most frequent cause by far. Cookies for a domain persist in the browser until they expire or are cleared. Over weeks or months of visiting a site, cookies from analytics, advertising, session management, and feature flags accumulate:
# Check your cookie size in DevTools console
document.cookie.length
If the number is above 4000, you are getting close to typical server limits. Above 8000, most servers will reject the request.
Subdomains make this worse. A cookie set on
.example.com
is sent to every subdomain:
www.example.com
,
app.example.com
,
api.example.com
,
cdn.example.com
. Third-party scripts that set cookies on the root domain pollute every subdomain’s requests.
The fix for users: clear cookies for the affected domain. The fix for developers: audit cookies regularly, set appropriate expiration times, scope cookies to specific paths and subdomains, and move analytics to server-side tracking where possible.
Large JWT tokens
JWT (JSON Web Token) authentication sends the full token in the
Authorization
header on every request. A JWT contains claims – user data, roles, permissions, metadata – and every claim increases the token size:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIx
MjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZXMiOlsiYWRtaW4iLCJlZGl0
b3IiLCJ2aWV3ZXIiXSwicGVybWlzc2lvbnMiOlsicmVhZDp1c2VycyIsIndyaXRlOnVzZ
XJzIiwicmVhZDpwb3N0cyIsIndyaXRlOnBvc3RzIiwiZGVsZXRlOnBvc3RzIl19...
A minimal JWT is around 300 bytes. But tokens that embed full permission sets, user profiles, or organization hierarchies can grow to 4-8KB. Multiply that by a few requests in flight and the header budget is consumed.
The fix: keep JWTs small. Store only essential claims (user ID, role names) in the token. Look up detailed permissions server-side. If your token needs to be large, consider opaque tokens (a random string that references a server-side session) instead of self-contained JWTs.
Proxy and CDN header accumulation
Each layer in a request chain adds headers:
Client -> CDN -> Load Balancer -> Reverse Proxy -> Application
CDN adds: CF-Connecting-IP, CF-Ray, CF-IPCountry, CF-Visitor
LB adds: X-Forwarded-For, X-Forwarded-Proto, X-Request-ID
Proxy adds: X-Real-IP, X-Forwarded-Host, X-Nginx-Proxy
This is normally fine. But misconfigured proxies can append instead of replace headers on each hop, causing them to grow with each request through the chain. A
X-Forwarded-For
header that should contain one IP ends up with a chain of IPs from every proxy:
X-Forwarded-For: 203.0.113.50, 10.0.0.1, 10.0.0.2, 10.0.0.3, 10.0.0.4
In extreme cases with many proxy hops or request retries, this can cause a 431.
Browser extensions
Extensions can inject headers into every request. Privacy extensions, ad blockers, VPN extensions, and developer tools each add their own headers. With enough extensions active, the combined header size crosses the limit.
Test by opening the site in an incognito window with all extensions disabled. If it works in incognito, an extension is the cause.
Server-specific limits and configuration#
Nginx
Nginx uses
large_client_header_buffers
to control header size limits:
# Default: 4 buffers of 8KB each
large_client_header_buffers 4 8k;
This means: Nginx allocates up to 4 buffers of 8KB each for request headers. A single header line (like the
Cookie
header) must fit in one buffer (8KB). The total of all headers must fit in the combined buffer space (32KB).
Note: Nginx returns
400 Bad Request
for oversized headers, not
431
. The 400 Bad Request: Request Header Or Cookie Too Large error is Nginx’s version of a 431.
To increase the limit:
http {
large_client_header_buffers 4 16k;
}
After changing, test and reload:
nginx -t && systemctl reload nginx
Increasing buffer sizes is a valid fix but it consumes more memory per connection. On high-traffic servers, doubling the header buffer from 8k to 16k across thousands of concurrent connections adds up. Fix the root cause (reduce cookie/header size) rather than just increasing limits.
Apache
Apache uses
LimitRequestFieldSize
for individual headers and
LimitRequestFields
for the number of headers:
# Default: 8190 bytes per header, 100 headers max
LimitRequestFieldSize 16384
LimitRequestFields 100
Apache returns
400 Bad Request
for oversized headers by default. Some configurations with mod_security or custom error handlers may return 431.
Node.js
Node.js has a default maximum header size of 16KB (raised from 8KB in Node 12):
# Check current limit
node -e "console.log(require('http').maxHeaderSize)"
# Start with a larger limit
node --max-http-header-size=32768 app.js
For Express applications, you can also set this in the server options:
const server = app.listen(3000, {
maxHeaderSize: 32768
});
Java/Jetty
The “bad message 431” error is specific to Jetty. Jetty’s default request header size is 8KB:
<!-- In jetty.xml or jetty-http.xml -->
<Set name="requestHeaderSize">16384</Set>
For Spring Boot applications using embedded Jetty:
server.max-http-request-header-size=16384
Tomcat
Tomcat’s default is 8KB for header size:
<!-- In server.xml, Connector element -->
<Connector port="8080" maxHttpHeaderSize="16384" />
For Spring Boot with embedded Tomcat:
server.max-http-header-size=16384
How to diagnose#
Step 1 – Check cookie size
Open browser DevTools, go to the Console tab, and run:
document.cookie.length
Then check the Application tab (Chrome) or Storage tab (Firefox) to see individual cookies and their sizes. If the total is above your server’s limit, cookies are the cause.
Step 2 – Check the full request headers
In the Network tab of DevTools, find the failed request and look at the Request Headers section. Look for:
- A
Cookieheader that is several KB - An
Authorizationheader with a large JWT - Many
X-headers from proxies or middleware - Unusual headers from browser extensions
Step 3 – Test in incognito
An incognito window starts with no cookies and most extensions disabled. If the request works in incognito, the cause is accumulated cookies or an extension.
Step 4 – Test with curl
curl sends no cookies by default (unless you specify a cookie jar). If the request works with curl but fails in the browser, the browser is sending extra data (cookies, extension headers) that pushes the request over the limit:
curl -I https://example.com/page-that-fails
Step 5 – Check server error logs
The server’s error log usually reports the specific reason:
# Nginx
tail -20 /var/log/nginx/error.log
# Look for: "client sent too long header line" or "too long request header"
# Node.js
# Look for: "Parse Error: Header overflow"
# Jetty
# Look for: "Header too large" or "bad message 431"
Fixing on the client side#
Clear cookies
The fastest fix. Clear cookies for the affected domain:
- Chrome: DevTools > Application > Cookies > right-click domain > Clear
- Firefox: Settings > Privacy & Security > Cookies and Site Data > Manage Data > find domain > Remove
- All browsers: incognito window bypasses the issue immediately
Disable browser extensions
If clearing cookies does not help, disable extensions one at a time to find which one is injecting large headers.
Reduce JWT token size
If you control the application:
- Remove unnecessary claims from the token
- Store detailed permissions server-side, reference them by role name in the token
- Consider opaque tokens for server-to-server communication where self-contained tokens are not needed
- Set shorter token expiry and refresh frequently rather than embedding everything to avoid round trips
Fixing on the server side#
Audit and reduce cookies
The permanent fix. Audit every cookie your site sets:
# List all cookies in DevTools console
document.cookie.split(';').forEach(c => {
const [name, val] = c.trim().split('=');
console.log(name, val ? val.length : 0, 'bytes');
});
For each cookie, ask:
- Is this cookie still needed?
- Can it be scoped to a specific path instead of the root domain?
- Can it be set on a subdomain instead of the root domain?
- Can the data be stored server-side (in a session or database) with only a session ID in the cookie?
- Can the expiration be shortened?
Increase server header limits
A valid short-term fix while you audit the root cause. See the server-specific sections above for Nginx, Apache, Node.js, Jetty, and Tomcat configuration.
Do not set limits excessively high. Large header limits increase memory usage per connection and can make the server more vulnerable to slowloris-style attacks where an attacker sends very large headers to exhaust server memory.
Fix proxy header duplication
If headers grow through a proxy chain, ensure each proxy replaces rather than appends:
# Nginx - set (replace) instead of add (append)
proxy_set_header X-Forwarded-For $remote_addr;
# Not this (appends on each hop):
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Use
$proxy_add_x_forwarded_for
only on the first proxy in the chain. Subsequent proxies should use
$remote_addr
to avoid the header growing with each hop.
431 vs related errors#
| Status code | What happened |
|---|---|
| 400 Bad Request | Request is malformed – bad syntax, encoding errors |
| 400 Request Header Or Cookie Too Large | Nginx-specific version of 431 (Nginx predates RFC 6585) |
| 431 Request Header Fields Too Large | Headers exceed server limits – clear cookies or reduce header size |
| 413 Request Entity Too Large | The request body (not headers) is too large – increase upload limits |
The key distinction: 431 is about request headers (metadata sent before the body). 413 is about the request body (the actual content being uploaded). They have completely different causes and fixes.
Summary#
A 431 Request Header Fields Too Large means the server rejected your request because the HTTP headers are too big. The most common cause is accumulated cookies – clear them and the error goes away immediately. For a permanent fix, audit your cookies, reduce JWT token sizes, or fix proxy header duplication. Increasing server header limits is a valid workaround but address the root cause first.