Diagnosing HTTP 413 Payload Too Large in Express
Clients get 413 before an Express route runs. Learn whether the limit comes from Express body parsing, a reverse proxy, or a CDN, and fix the right layer.
04 Jul 2025, 15:17 UTC

Clients uploading JSON or form data to an Express API receive HTTP 413 Payload Too Large before the route handler runs. The request never reaches your application code, so logging inside the route cannot explain the failure. The useful takeaway is that 413 can be enforced by Express itself, by a reverse proxy in front of Node, or by a CDN/WAF. Raising the Express limit alone does not fix rejections that happen upstream.
Recognize the condition
- The client receives HTTP 413 Payload Too Large.
- The response arrives quickly, often before any application log line for the request.
- An Express-originated response may include application headers; a proxy-originated response often returns generic HTML or closes the connection.
- Some payload sizes succeed while similar ones fail, which usually points to a size threshold rather than a code bug.
Cause and diagnostic table
| Layer | Typical signal | Where to look |
|---|---|---|
Express body parser (express.json, express.urlencoded, express.raw) | 413 returned by Node; request appears in Express access logs; custom error handler sees err.type === 'entity.too.large' | Application source and error-handling middleware |
| Reverse proxy (Nginx, Apache, cloud load balancer) | 413 or connection reset; generic HTML error page; no Express request log for the attempt | Proxy configuration and access/error logs |
| CDN or WAF | 413 or blocked response at the edge; origin logs show no request | CDN/WAF dashboard and edge logs |
Ordered checks to isolate the rejecting layer
Check 1: Reproduce directly against Node
Bypass any proxy and send payloads just below and just above the suspected limit to the Node port.
# Run from a terminal that can reach the Node port, for example localhost:3000.
# No special permissions are required for client-side curl on unprivileged ports.
curl -X POST http://localhost:3000/upload \
-H "Content-Type: application/json" \
--data-binary @payload.json
Placeholders: replace http://localhost:3000/upload with your route and payload.json with a test file. Expected checks: a payload just under the limit should reach the route or return an application error; a payload just over the limit should return 413. Compare the status code, headers, and response body between direct and proxied requests. Risk: oversized payloads consume memory; do not send multi-megabyte test payloads to a production process.
Check 2: Confirm the Express parser and its limit
# Run in the application directory.
node -e "console.log(require('express/package.json').version)"
In Express 4.x, express.json() and express.urlencoded() use body-parser and default to a 100kb request-body limit; express.raw() maintains its own independent limit. Inspect your source for calls such as express.json({ limit: '10mb' }). The JSON and URL-encoded parsers are separate, so one Content-Type can fail while another succeeds.
Check 3: Inspect a custom error handler
app.use((err, req, res, next) => {
if (err.type === 'entity.too.large') {
console.error('Express body parser rejected payload', err.status);
return res.status(413).json({ error: 'payload too large' });
}
next(err);
});
Place this after the body parsers. If err.type is entity.too.large, the rejection originated in Express. If no handler runs and no application log appears, the request likely failed before reaching Node.
Check 4: Review reverse-proxy configuration
# Run on the proxy host. sudo may be required to read /etc/nginx.
grep -R "client_max_body_size" /etc/nginx/
Expected check: a directive such as client_max_body_size 1m; means Nginx rejects larger bodies before forwarding to Express. Apache uses LimitRequestBody in bytes. Cloud load balancers have similar maximum body size settings. If the proxy threshold is lower than the Express threshold, the proxy is the bottleneck.
Check 5: Check the CDN or WAF
If direct-to-Node requests succeed but proxied requests fail, and the proxy configuration allows the size, inspect the CDN/WAF dashboard and edge logs for a maximum request-body rule. Edge rejections produce no Express logs.
Fixes tied to the finding
If Express is rejecting the payload
Raise the limit for the affected parser:
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ limit: '10mb', extended: true }));
Set both JSON and URL-encoded limits explicitly to avoid mismatches. Do not remove the limit entirely: buffering oversized bodies in memory can exhaust the process. For file uploads, prefer streaming parsers that write to disk or object storage with their own size limits rather than increasing express.json to accommodate files.
If the reverse proxy is rejecting the payload
Nginx example:
client_max_body_size 20m;
Place the directive in the http, server, or location context, then run nginx -t and reload. Reloading may require sudo:
nginx -t && nginx -s reload
Apache uses LimitRequestBody 20971520 (bytes) in the appropriate config context, followed by a reload. For managed load balancers, update the maximum body size in the provider console.
If the CDN or WAF is rejecting the payload
Adjust the maximum request-body size in the CDN/WAF dashboard or create an allow rule for the affected origin route. Changes may take time to propagate to edge nodes.
Limitations
- Raising the body limit increases memory exposure; 413 exists partly to prevent memory exhaustion from oversized payloads.
- Bypassing body parsing for specific routes means downstream middleware that expects
req.bodymay break. - Default limits can vary across Express and body-parser releases; verify the installed version instead of assuming a fixed threshold.
- Streaming parsers are safer for large uploads but change how your route accesses the data.
Practical verification
Create two JSON test files, one just below and one just above the configured limit. Send each directly to Node and then through the proxy:
curl -X POST http://localhost:3000/upload -H "Content-Type: application/json" --data-binary @small.json
curl -X POST http://localhost:3000/upload -H "Content-Type: application/json" --data-binary @large.json
Expected result: the smaller file reaches the application or returns an application-level error; the larger file returns 413. If direct-to-Node accepts the larger file but the proxy returns 413, the proxy or CDN is enforcing the lower limit. If both return 413 at the Express limit, raise the Express parser limit and retest.
Escalation criteria
- Escalate to the infrastructure or platform team when there are no Express access logs for the request, the proxy returns generic HTML, the connection resets before a response, or the limit is controlled by a CDN/WAF outside your repository.
- Keep the fix in application code when the Express error handler reports
entity.too.large, logs show the request was received, and changingexpress.json({ limit })resolves direct-to-Node tests.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.