413 Request Entity Too Large (tested)
413 means the server refused the request because the body was bigger than it allows. The status was called Request Entity Too Large, then Payload Too Large (Node.js still sends that text), and is Content Too Large in RFC 9110. The limit is never in the client. It is set by whatever reads the body first: a body parser, a proxy, or the language runtime. We measured where Node.js and Express draw the line and what the client sees.
1. Plain Node.js has no body limit
An http.createServer handler that just counted bytes accepted a 200 MB POST (209,715,200 bytes) with 200 in 0.18 s on localhost. Node.js limits header size, not body size. If a Node app returns 413, a framework or middleware sent it. For the header-size equivalent see Request Header Or Cookie Too Large.
2. Express: the 100kb default
express.json(), express.urlencoded(), express.raw() and express.text() all default to limit: '100kb', and kb means 1,024 bytes. Results with curl and --data-binary:
| Parser | Body size | Status |
|---|---|---|
express.json() | 101,376 bytes (99 KB) | 200 |
express.json() | 102,400 bytes (exactly 100 KB) | 200 |
express.json() | 102,401 bytes | 413 |
express.json() | 103,424 bytes (101 KB) | 413 |
express.urlencoded() | 103,424 bytes | 413 |
express.raw() | 102,400 / 103,424 bytes | 200 / 413 |
express.text() | 103,424 bytes | 413 |
| no parser on the route | 103,424 bytes | 200 |
express.json({ limit: '10mb' }) | 103,424 bytes | 200 |
express.json({ limit: '10mb' }) | 10,485,760 / 10,485,761 bytes | 200 / 413 |
Two cases that surprised us:
- Compressed bodies count after decompression. The 103,424-byte JSON gzipped to 155 bytes. Sent with
Content-Encoding: gzip, it still got413. Compressing the upload does not get you under the limit. - Chunked bodies are counted too. With
Transfer-Encoding: chunkedand noContent-Length, the same body got413. The error just has nolengthfield, because no length was declared.
A body with the wrong Content-Type (for example text/plain sent to express.json()) is not checked for size at all. The parser skips it and req.body is undefined in Express 5, which in our route caused a 500. If a large upload "works" but the data is missing, check the header: Content-Type: application/json.
3. What the 413 response looks like
HTTP/1.1 413 Payload Too Large
X-Powered-By: Express
Content-Security-Policy: default-src 'none'
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
Content-Length: 994
The default error handler returns HTML. With NODE_ENV=production the body is just <pre>Payload Too Large</pre>. Otherwise it includes the stack trace, starting with PayloadTooLargeError: request entity too large at readStream (.../raw-body/index.js). The error object passed to your error middleware had these fields:
{ "name": "PayloadTooLargeError", "type": "entity.too.large",
"status": 413, "limit": 102400, "length": 103424, "expected": 103424,
"message": "request entity too large" }
Check err.type === 'entity.too.large' to return a JSON error your client can show:
app.use(express.json({ limit: '10mb' }));
app.use((err, req, res, next) => {
if (err.type === 'entity.too.large') {
return res.status(413).json({ error: 'Body too large', limit: err.limit });
}
next(err);
});
Raise the limit only on the routes that need it, for example app.post('/upload', express.json({ limit: '10mb' }), handler). For real file uploads, use streaming multipart handling instead of a large JSON limit.
4. In the browser: 413 can show up as a CORS error
We sent a 103,424-byte JSON body with fetch from a page on localhost:8983 to an Express API on localhost:8984, in Chromium 153:
Where the API sets Access-Control-Allow-Origin | Small body | 103,424-byte body |
|---|---|---|
Only in the route handler (after express.json()) | 200 | TypeError: Failed to fetch; the console says "blocked by CORS policy: No 'Access-Control-Allow-Origin' header" |
In middleware that runs before express.json() | 200 | res.status 413, statusText "Payload Too Large", JSON body readable |
The parser rejects the request before your handler runs, so headers that your handler would have set are never sent. Put the CORS middleware (for example app.use(cors())) before the body parsers. Then the frontend gets a real 413 it can handle, instead of a network error.
5. Other servers (from their docs, not measured here)
- nginx:
client_max_body_sizedefaults to1m. Larger requests get413from nginx before your app sees them. Set it inhttp,serverorlocation, for exampleclient_max_body_size 10m;.0disables the check. If nginx sits in front of Express, raise both limits. - PHP:
upload_max_filesize(default 2M) andpost_max_size(default 8M) in php.ini.post_max_sizemust be at least as large as the upload. The web server in front of PHP has its own limit too.
6. Checklist
- Find who sent the 413. An Express HTML body with
Content-Security-Policy: default-src 'none'comes from Express. A different error page (for example one naming nginx) means a proxy in front rejected it. - Measure the real body size in bytes, after decompression.
- Raise the limit on the layer that rejected it:
express.json({ limit: '10mb' }), nginxclient_max_body_size, etc. - Return a JSON 413 with CORS headers so the browser can read it.
Frequently asked questions
What is Express's default body size limit?
100kb, which is 102,400 bytes. In Express 5.2.1 express.json() accepted exactly 102,400 bytes and returned 413 at 102,401. express.urlencoded(), express.raw() and express.text() have the same default.
How do I fix PayloadTooLargeError: request entity too large?
Pass a larger limit to the parser that rejected the body, for example express.json({ limit: '10mb' }), and if a proxy like nginx sits in front, raise its client_max_body_size too. With limit '10mb', 10,485,760 bytes passed and 10,485,761 got 413.
Does Node.js have a request body size limit?
No. A plain http.createServer handler on Node.js 24.21.0 accepted a 200 MB body. A 413 from a Node app comes from a framework, middleware or proxy.
Does gzip compression get around the 413 limit?
No. A 103,424-byte JSON body gzipped to 155 bytes still got 413 from express.json(), because the limit is applied to the decompressed size.
Why does fetch say 'Failed to fetch' instead of 413?
The 413 response had no Access-Control-Allow-Origin header, because the body parser rejected the request before the handler that sets it ran. Chromium 153 reported a CORS error. Moving CORS middleware before express.json() made the 413 readable.
Written with AI assistance. Measured on 2026-10-02 with Node.js 24.21.0 (plain http.createServer on localhost:8983), Express 5.2.1 with body-parser 2.3.0 (localhost:8984, NODE_ENV development and production), curl 8.14.1 as the client, and headless Chromium 153.0.8010.12 (Playwright) for the browser fetch test. Request bodies were exact byte counts generated with Python 3.13.5. Flask could not be installed here (no pip) and nginx and PHP are not installed, so their settings are quoted from their documentation, not run. PlainHeaders is our extension; Node.js, Express, curl and Chromium are free.