Troubleshooting

HTTP error 431 Request Header Fields Too Large: which server sends it, and how to fix it

HTTP error 431 means the request headers, usually cookies, are too large. Fix it in Chrome, raise the 16 KB Node.js limit, and see why nginx sends 400.

Published ~12 min read
On this page

Short answer

431 Request Header Fields Too Large means the request headers, almost always the Cookie header, are bigger than the server accepts. As a visitor, delete that one site’s cookies and reload. As the owner, find which server in the chain answered (usually Node.js, whose limit is 16 KB), shrink the cookies, or raise the limit with --max-http-header-size.

Where to start

What does HTTP error 431 mean?

Status 431 was added to HTTP in 2012 by RFC 6585, section 5. A server sends it when it refuses to read the request because the header section as a whole, or one single header field, is larger than it is willing to process. The client is allowed to try again with smaller headers. Nothing is wrong with the URL, the page or the server’s health: the request was simply too big before the body even started.

Three neighbouring codes are easy to mix up: 413 Content Too Large means the request body is too big (an upload), 414 URI Too Long means the URL is too long (nginx, Apache, Kestrel and Jetty send it for an oversized request line), and 431 means the headers are too big.

In practice the oversized header is usually the Cookie header. Browsers send every cookie stored for a host with every request to it, and RFC 6265 asks browsers to support cookies of at least 4,096 bytes each and at least 50 per domain. Five cookies of 4 KB are already more than 16 KB, which is exactly where Node.js stops. The rest are long Referer URLs full of tracking parameters, big single sign-on or Kerberos tokens, and proxies that keep adding forwarding headers.

Key facts

Status line
431 Request Header Fields Too Large
Defined in
RFC 6585, section 5 (2012)
Chrome shows
HTTP ERROR 431
Usual cause
Too many or too large cookies
Node.js default limit
16 KB (16,384 bytes), all headers togetherSince Node 13.13.0; 8 KB before
nginx default
4 buffers of 8 KB, answers 400 or 414, never 431
Apache default
8,190 bytes per field, 100 fields, answers 400
Cloudflare edge
128 KB of request headersRaised from 32 KB in October 2025
Also send 431
Kestrel (32 KB), Jetty (8 KB), Go, Python http.server
Answer 400 instead
Tomcat and Spring Boot (8 KB), IIS (16 KB)

So the status code itself tells you something: a 431 almost always comes from an application server, not from the web server in front of it. The servers that send it, and the ones that send something else for the same problem, with the defaults checked against each project’s documentation or source on November 2, 2026:

ServerDefault limitStatus when exceededSetting
Node.js (Express, Next.js, Vite, Nuxt dev servers)16 KB for the request line and all headers431, empty body--max-http-header-size, maxHeaderSize
Go net/http1 MB (DefaultMaxHeaderBytes)431http.Server{MaxHeaderBytes: ...}
ASP.NET Core Kestrel32 KB for all headers, 100 headers, 8 KB request line431 (414 for the request line)Limits.MaxRequestHeadersTotalSize
Jetty8 KB for the request line and all headers431 (414 for the request line); Jetty 9 to 11 show Bad Message 431requestHeaderSize
Python http.server65,536 bytes per line, 100 headers431 Line too long / 431 Too many headers (414 for the request line)none (development server)
nginx8 KB per header line, 4 buffers of 8 KB400, or 414 for a long request linelarge_client_header_buffers
Apache httpd 2.48,190 bytes per field, 100 fields, 8,190 for the request line400 (414 for the request line)LimitRequestFieldSize, LimitRequestFields
Tomcat, Spring Boot (default server)8 KB for the request line and all headers400, logged as Request header is too largemaxHttpHeaderSize, server.max-http-request-header-size
IIS (HTTP.sys)16 KB per header, 16 KB for request line plus headers400MaxFieldLength, MaxRequestBytes in the registry
Cloudflare128 KB of request headers in total413 per Cloudflare's changelog; a reset connection in our testnot configurable

How do I fix HTTP error 431 in Chrome?

Chrome shows its own “This page isn’t working” screen with HTTP ERROR 431 underneath, because Node.js sends the status with an empty body and the browser has nothing else to display. The fix on your side is the same as for a cookie-sized 400:

  • Test in an incognito window. It starts without your cookies. If the page loads there, your stored cookies for that site are the problem.
  • Delete the cookies of that one site. Click the icon to the left of the address bar, open the cookies or site settings entry, and delete the data for that site. You can also open chrome://settings/content/all, search for the domain and delete its data there. You do not need to clear your whole browser history.
  • Sign out and sign in again. A fresh login replaces piled-up session and token cookies with new, usually smaller ones. If the error shows up right after a single sign-on login (Google, Microsoft, Okta and the like), that is the typical pattern: the identity provider’s redirect adds large cookies or state to the request, and the site’s own cookies push it over the limit.
  • Shorten the link. If you came from a link with a very long query string, open the plain address without everything after the ?. The long URL travels in the Referer header of the next requests.
  • Turn off extensions that add headers (header editors, some VPN and privacy add-ons) and reload.

If the site still fails in a fresh private window, the problem is on the server side and only its operator can fix it.

Reproduce 431 with curl

Before you change any limit, make the error happen on demand. A cookie made of a few thousand a characters does it. The status codes below are the ones a default Node.js 26 server returned in our test:

cookie inflation test
# a 16,000-character cookie still fits, 20,000 does not
curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Cookie: big=$(head -c 16000 /dev/zero | tr '\0' a)" http://127.0.0.1:3000/
200
curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Cookie: big=$(head -c 20000 /dev/zero | tr '\0' a)" http://127.0.0.1:3000/
431

The full response shows how little Node.js says about it: a status line, a Connection: close and no body, no Server header, no hint which header was too big.

curl -si · Node.js 26
HTTP/1.1 431 Request Header Fields Too Large
Connection: close

Real browser cookies trip it the same way. Four cookies of 4,000 bytes (16,018 bytes in the Cookie header) still got a 200 from the default server; a fifth one turned it into 431. To see what a real browser sends, open the developer tools, Network tab, right-click the failing request and choose Copy as cURL. The -H 'cookie: ...' part is your cookie header, and pasting the command into a terminal reproduces the error with the exact same headers. Count its size like this:

how big is the Cookie header?
# paste the cookie value between the quotes
printf '%s' 'session=...; _ga=...; consent=...' | wc -c

document.cookie.length in the console is a quick first look, but it leaves out HttpOnly cookies, which are often the biggest ones (sessions, tokens). Copy as cURL includes them. For more ways to send custom headers from the command line, see the curl examples.

Fix 431 in Node.js (Express, Next.js, Vite)

Node.js is the most common sender of 431, and it is behind most “431 localhost” reports, because Express, Next.js, Vite and Nuxt all serve your app through Node’s HTTP server. Its limit, --max-http-header-size, defaults to 16,384 bytes since Node 13.13.0 (it was 8 KB before, which is why older articles still say 8 KB). The limit covers the request line and all headers together, not one header: in our test a 17,000-character URL with no cookie at all also got 431, where nginx would have answered 414.

Check the value your Node.js uses:

read the active limit
node -p "require('http').maxHeaderSize"
16384

Raise it for one process

node flag
node --max-http-header-size=32768 server.js

For tools you do not start with node yourself (next dev, vite, npm start), pass the flag through NODE_OPTIONS, which accepts it. Every Node process started from that shell inherits it:

Linux · macOS
NODE_OPTIONS='--max-http-header-size=32768' npm run dev
Windows PowerShell
$env:NODE_OPTIONS = "--max-http-header-size=32768"
npm run dev
Windows cmd
set NODE_OPTIONS=--max-http-header-size=32768
npm run dev

To keep it with the project, put the flag into the script in package.json, for example "start": "node --max-http-header-size=32768 server.js", or into the Environment= line of the systemd unit that runs the app.

Raise it in code for one server

http.createServer takes a maxHeaderSize option that overrides the flag for that server only. With Express, create the server yourself instead of calling app.listen:

server.js · Express
const http = require("http");
const express = require("express");

const app = express();
app.get("/", (req, res) => res.send("ok"));

http.createServer({ maxHeaderSize: 32768 }, app).listen(3000);

In our test the server with 32,768 accepted a 32,000-character cookie and answered 431 at 33,000, and NODE_OPTIONS with 65,536 moved the line to just under 64 KB. Prefer 16 KB steps over a huge number: every connection may hold that much header data in memory, and a server that accepts megabytes of headers is easier to tie up. If 32 KB is not enough, the cookies are the bug.

Why localhost collects so many cookies

Cookies are not separated by port. RFC 6265 says so in section 8.5, and browsers follow it: every app you have run on localhost:3000, localhost:5173 or localhost:8080 stores its cookies for the host localhost, and all of them are sent to whichever dev server you open next. A few auth libraries, an analytics snippet and an old project’s session cookie add up fast. Two clean ways out:

  • Clear the site data for localhost (developer tools, Application tab, Storage, Clear site data). The 431 disappears at once if cookies were the cause.
  • Give each project its own host name: 127.0.0.1 keeps a separate cookie jar from localhost, and Chrome resolves names like shop.localhost to your own machine, so every project gets its own cookies.

431 with no cookies at all: a proxy loop

If the error stays in a private window with every cookie gone, check the proxy setting of your dev server. A proxy entry whose target is the dev server itself (the same port, or a host name that resolves back to it) makes the request travel in a circle. Proxy middleware that adds X-Forwarded-For and similar headers makes the request a little longer on every pass, until Node’s 16 KB limit stops it and the 431 comes back to the browser. Point the proxy at the API’s real port, and the error is gone without touching any limit. A raised limit only makes such a loop run longer.

Fix 431 in ASP.NET Core and Java (Kestrel, Jetty, Tomcat, Spring Boot)

These limits and status codes come from each project’s source and documentation, checked on November 2, 2026, not from our own test. ASP.NET Core’s Kestrel answers 431 when all request headers together pass MaxRequestHeadersTotalSize (32 KB) or when there are more than 100 of them, and 414 when the request line passes MaxRequestLineSize (8 KB). Raise it in Program.cs:

Program.cs · ASP.NET Core
builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestHeadersTotalSize = 65536;
});

Behind IIS, HTTP.sys reads the request before your app does, and it answers 400 at its own 16 KB per header (MaxFieldLength, MaxRequestBytes under HKLM\System\CurrentControlSet\Services\HTTP\Parameters). The 400 guide covers that registry change.

In Java, the answer depends on the embedded server. Jetty counts the request line and all headers against requestHeaderSize, 8 KB by default, and sends 431 (414 while it is still reading the URL); Jetty 9 to 11 show the page as Bad Message 431. Tomcat, which Spring Boot uses unless you swap it, has the same 8 KB but answers 400, and its log shows Request header is too large. So a Spring Boot app gives 400 on Tomcat and 431 on Jetty for the same request. Spring Boot sets both through one property, called server.max-http-header-size before Spring Boot 3.0. A proposal to raise the 8 KB default to 16 KB was declined in 2025, so it is still 8 KB:

application.properties · Spring Boot 3 and later
server.max-http-request-header-size=16KB
conf/server.xml · standalone Tomcat
<Connector port="8080" protocol="HTTP/1.1"
           maxHttpHeaderSize="16384" />
standalone Jetty 12 · from your Jetty base
java -jar $JETTY_HOME/start.jar jetty.httpConfig.requestHeaderSize=16384

Behind nginx: why you see 400, not 431

nginx never sends 431. It reads request headers into a 1 KB buffer and then into large_client_header_buffers, 4 buffers of 8 KB by default. A header line longer than one buffer gets 400 Request Header Or Cookie Too Large, and a request line longer than one buffer gets 414. Since the limits also apply to HTTP/2 and HTTP/3, switching protocols changes nothing. That variant has its own guide: 400 Request Header Or Cookie Too Large.

With nginx in front of a Node.js app, the smallest limit in the chain answers, with its own status code. One cookie line over 8 KB: nginx answers 400 and Node never sees the request. Headers of 20 KB spread over lines that each fit in 8 KB: nginx passes them on, Node.js refuses with 431, and nginx hands that 431 to the browser. Raise both, and keep Node’s limit at least as large as what nginx lets through:

/etc/nginx/nginx.conf · inside the http block
large_client_header_buffers 4 16k;
Debian · Ubuntu
sudo nginx -t && sudo systemctl reload nginx

Set it in the http block. Set only inside one server block, nginx may use the default server’s value for connections on that address, and the change seems to do nothing. To tell who answered, look at the response: nginx’s 400 page is a short HTML page that carries nginx (often with its version) in the footer, while Node’s 431 has no body and no Server header. If you are setting up the proxy itself, the nginx reverse proxy guide has the full server block.

Apache: 400 “Size of a request header field exceeds server limit”

Apache httpd also answers oversized headers with 400, not 431, and says why on the page: Size of a request header field exceeds server limit. The limits are LimitRequestFieldSize (8,190 bytes per header field), LimitRequestFields (100 fields) and LimitRequestLine (8,190 bytes for the method and URL). Apache’s own documentation points out that SPNEGO (Kerberos) authentication headers alone can reach 12,392 bytes, so Windows domain logins (Kerberos) can trip the limit on their own.

/etc/apache2/apache2.conf (Debian, Ubuntu) or httpd.conf
LimitRequestFieldSize 16380
LimitRequestLine 16380
Debian · Ubuntu
sudo apachectl configtest && sudo systemctl reload apache2

Put the directives in the main server config. With name-based virtual hosts Apache takes the value from the first virtual host for that IP address and port, so a line inside a later VirtualHost is silently ignored. Apache in front of Node.js with mod_proxy behaves like the nginx chain above: whichever limit is smaller answers.

Behind Cloudflare

Cloudflare raised its request header limit to 128 KB in total in October 2025; before that it was 32 KB in total and 16 KB per header, the figures many older guides still quote. Its changelog says requests over the limit get a 413. We sent growing cookies to our own Cloudflare-proxied site over HTTP/1.1: a 100 KB cookie passed the edge, and at 140 KB the connection was reset without any status page. Either way, 128 KB is far above what nginx, Apache or Node.js accept by default, so a 431 on a site behind Cloudflare almost always comes from your origin. Check the response: the server: cloudflare header is set on everything the edge relays, so look at the body and your origin’s logs to find who refused.

The opposite direction has its own error. When your origin sends response headers that are too big, for example dozens of Set-Cookie lines, Cloudflare shows error 520 instead.

Raising a limit buys time; small headers fix the cause. On your own application:

  • Keep a session ID in the cookie, not the session. Store user data, carts and feature flags on the server, keyed by a short ID. A JWT that gains a claim for every role or setting is the typical cookie that grows until it breaks.
  • Scope cookies tightly. A cookie set with Domain=example.com goes to every subdomain; leave out Domain to keep it on one host, and set a Path where only part of the site needs it.
  • Expire what nobody reads. Give every cookie a Max-Age and remove cookies of features you retired by setting them again with Max-Age=0.
  • Audit third-party tags. Analytics, A/B testing and consent tools each add cookies on your domain. List them in the developer tools (Application tab, Cookies) and sort by size.
  • Watch cross-origin calls. A 431 from your API carries no Access-Control-Allow-Origin header, so the browser reports it as a CORS error and hides the real status. Check the Network tab before you touch your CORS settings.

Symptom, cause, fix

  1. Symptom: Chrome: “This page isn’t working”, HTTP ERROR 431

    Cause
    The cookies stored for that site are larger than the server accepts.
    Fix
    Open the page in incognito; if it loads, delete that one site's cookies and reload.
  2. Symptom: 431 from your dev server on localhost

    Cause
    Cookies from every app you ran on localhost, on any port, exceed Node's 16 KB.
    Fix
    Clear site data for localhost, or start the server with NODE_OPTIONS='--max-http-header-size=32768'.
  3. Symptom: 431 even in a private window with no cookies

    Cause
    A dev-server proxy forwards to itself and the headers grow on every pass, or the URL is very long.
    Fix
    Point the proxy at the real API port; shorten the query string.
  4. Symptom: 400 Request Header Or Cookie Too Large from nginx

    Cause
    One header line is over 8 KB, nginx's buffer size.
    Fix
    nginx.conf · http block
    large_client_header_buffers 4 16k;
  5. Symptom: Still 431 after raising nginx's buffers

    Cause
    The app behind nginx (Node.js) has the smaller limit and answers first.
    Fix
    Raise Node too: node --max-http-header-size=32768 server.js.
  6. Symptom: Spring Boot or Tomcat: 400, log says Request header is too large

    Cause
    Tomcat's 8 KB limit for the request line and headers was exceeded.
    Fix
    Set server.max-http-request-header-size=16KB (Spring Boot) or maxHttpHeaderSize on the Connector.
  7. Symptom: 400: Size of a request header field exceeds server limit

    Cause
    Apache's LimitRequestFieldSize (8,190 bytes) was exceeded, often by Kerberos tokens.
    Fix
    Set LimitRequestFieldSize 16380 in the main server config and reload.

A 431 is one of several 4xx answers that are about the request rather than the page. If the server accepts the headers but refuses the HTTP method, you get 405 Method Not Allowed.

On your own server you control every limit in the chain. On a Standard KVM with root access you set nginx’s buffers and the Node.js flag yourself, and the Node.js install is covered in how to install Node.js.

How we tested

OS image
Debian 12
Versions
Node.js 26.12.1curl 7.88.1Python 3.11.2
Tested on
Where
Test servers bound to 127.0.0.1 on our own Linux host; the Cloudflare check against vmheaven.io
Method
Default, flag, NODE_OPTIONS and maxHeaderSize servers hit with growing Cookie headers from curl; thresholds found by bisection

Frequently asked

What does HTTP error 431 mean?

431 Request Header Fields Too Large means the server refused the request because its headers, all together or one single header, are bigger than it accepts. The oversized header is usually Cookie: too many or too large cookies stored for that site. The page and the server are fine; a request with smaller headers goes through.

How do I fix HTTP error 431 in Chrome?

Open the page in an incognito window. If it loads there, sign out and sign in again, or delete the cookies of that one site through the icon to the left of the address bar (or chrome://settings/content/all) and reload. If you arrived through a very long link, open the plain address without the query string. If incognito fails too, only the site's operator can fix it.

Why do I get error 431 on localhost?

Your dev server runs on Node.js, which refuses more than 16 KB of request headers by default, and cookies are not separated by port: every app you ever ran on localhost sends its cookies to every other one. Clear the site data for localhost, or start the server with NODE_OPTIONS='--max-http-header-size=32768'. If it happens with no cookies at all, check for a dev-server proxy that forwards to itself.

How do I increase the max header size in Node.js?

Start Node with --max-http-header-size=32768 (bytes), set the same flag in NODE_OPTIONS for tools like next dev or vite, or pass { maxHeaderSize: 32768 } to http.createServer for one server. The default has been 16,384 bytes since Node 13.13.0. Check the active value with: node -p "require('http').maxHeaderSize".

Is 431 the same as 400 Request Header Or Cookie Too Large?

Same problem, different server. nginx and Apache answer oversized headers with 400 (nginx with the message Request Header Or Cookie Too Large) and never send 431, and so do Tomcat (the default server of Spring Boot) and IIS. Node.js, ASP.NET Core Kestrel, Jetty, Go and Python's http.server send 431. Behind a reverse proxy, the smallest limit in the chain answers, so raise nginx's large_client_header_buffers and the app server's limit together.

Related articles

All troubleshooting