Distributed systems
413 Payload Too Large — identify which layer rejected the body
Written and reviewed by Sahil Srivastav
413 Payload Too LargeWhat this error actually means
A receiver refuses the request because its content exceeds a limit it is willing to process. The familiar reason phrase is Payload Too Large; newer HTTP semantics call status 413 Content Too Large, and older servers may display Request Entity Too Large. The numeric status is the stable identifier across those implementations.
Every body-processing hop can impose its own ceiling: edge provider, ingress, reverse proxy, framework parser, multipart parser and business validation. Raising the application setting does nothing when nginx rejects the request before forwarding it. Raising nginx does nothing when a JSON parser later buffers the whole body and rejects or exhausts memory.
The file’s size is not necessarily the request’s size. Multipart encoding adds boundaries and fields; base64 representation grows binary content; JSON adds structure; compression creates a separate distinction between bytes transferred and bytes expanded. Define which quantity the product contract limits before editing an arbitrary number.
Causes, most common first
- 1The first proxy’s body limit is lower than the application contract. A default or inherited limit remains on the upload route. Multiple ingress definitions or location blocks make it easy to edit a setting that never applies to the actual request path.
- 2The framework parser rejects before the route handler. JSON, form and multipart middleware have independent limits. A handler-level size check runs too late if middleware already attempted to buffer and parse the entire body.
- 3Encoding or multipart fields exceed the assumed size. The UI checks the selected file but the request includes extra form parts or an expanded text encoding. Near-threshold failures are especially easy to misread when one layer uses decimal units and another configuration uses binary-sized units.
- 4A compressed request expands beyond a safe budget. The transferred body is small but decompression or parsing creates a much larger representation. A limit on wire bytes alone does not bound CPU or memory consumption from hostile or accidental expansion.
When you see it
- Uploads fail above a repeatable size while smaller requests to the same route succeed.
- There is a proxy access-log entry but no application request entry for the rejected upload.
- A raw binary upload succeeds while its base64-in-JSON equivalent is rejected.
- Only one location, ingress or deployment environment fails despite identical application code.
How to diagnose it
Step 1
Measure the actual input and request representation
Start with a local, non-sensitive fixture. For multipart uploads, the wire body exceeds this file size; for JSON, measure the complete serialised JSON. Avoid comparing the displayed browser filename size with a different representation sent by the client.
wc -c ./upload.binStep 2
Capture the response before following redirects
Use a disposable upload destination and replace the example URL. Preserve headers and inspect the response body for the rejecting layer. curl’s Expect handling may let a server reject from Content-Length before the client sends the whole body.
curl -sv --max-time 60 -H 'Content-Type: application/octet-stream' --data-binary @./upload.bin https://api.example.com/uploads/testStep 3
Inspect the matching nginx location and inherited limit
Read the full enclosing server and location blocks, not only the matching directive line. The effective value comes from the request’s actual configuration context. Pair it with error logs and application traces to establish whether the body reached the parser.
nginx -TStep 4
Find the threshold in a controlled environment
Try fixtures just below and above the expected ceiling using the same encoding as production. If direct-origin requests pass and public requests fail, compare edge and ingress limits. Do not generate progressively huge production uploads merely to discover a cap.
The fix
Choose a product limit and express it at every relevant boundary. Keep the edge cap large enough for permitted multipart overhead while enforcing the business file limit inside the upload workflow. Configure only the endpoint that needs the larger allowance; a global unlimited body setting makes every route an expensive buffering target.
Stream large bodies to bounded storage with backpressure instead of collecting them in memory. Apply cumulative byte limits during reads even when Content-Length is absent or inaccurate. Count decompressed bytes where compression is accepted, and bound multipart part count and field sizes separately.
For large objects, use an authorised direct-to-object-storage or resumable upload flow with short-lived scoped permissions. Verify the final object’s size, type and integrity before exposing it to the application. Moving bytes outside the API process does not remove the need for validation and cleanup of abandoned uploads.
Return an actionable error that states the supported limit and representation. A client cannot repair a deterministic oversized body by retrying it unchanged. If rejection is temporary, Retry-After can communicate when to try again; otherwise split, compress appropriately, or use the supported upload workflow.
# Example inside the existing nginx server block; validate with nginx -t.
location /uploads/ {
client_max_body_size 20m;
proxy_pass http://127.0.0.1:8080;
}
# The application still validates file bytes, parser limits and storage quota.How to stop it coming back
- Keep boundary tests for the complete encoded request, including multipart metadata and optional fields.
- Track 413 responses by rejecting layer so support can distinguish a product cap from accidental proxy drift.
- Load-test permitted uploads concurrently and measure memory, temporary-disk usage and slow-client behaviour.
FAQ
Should client_max_body_size be set to 0?
That disables this nginx size check. Use it only when another deliberately engineered boundary enforces the complete resource contract. A bounded route-specific limit is usually easier to operate and protects against accidental large bodies.
Will chunked transfer bypass the limit?
It should not. A receiver can count bytes as they arrive and reject once the limit is exceeded. The absence of Content-Length changes when the rejection can happen, not the endpoint’s permitted content size.
Why does the browser call this a CORS error?
A proxy-generated 413 may lack the CORS headers the application normally adds. Inspect the Network panel and proxy logs to find the underlying response. Fixing CORS alone does not make the body acceptable.
Related
Other errors engineers hit next to this one
- exec format error — container architecture mismatch
- no space left on device — container layers
- SIGTERM misses the application — shutdown ends in SIGKILL
- CommitFailedException: Commit cannot be completed since the group has already rebalanced
- Consumer group stuck rebalancing — poll timeout has expired
- The same message processed twice (at-least-once delivery)
- Messages processed out of order across partitions
- Webhook delivered twice — customer charged twice