Use HTTP 413 when the request is too large; use HTTP 400 when the request is malformed. That single distinction saves hours of log hunting, bad retries, and confused client teams. HTTP 413 is officially described as Content Too Large, meaning the server is refusing the request because its body exceeds a configured limit [source: MDN]. HTTP 400, by contrast, says the server could not understand the request at all.
TLDR: HTTP 413 points to size: uploaded files, JSON bodies, base64 images, or multipart forms that exceed a limit. HTTP 400 points to structure: broken JSON, invalid headers, bad encoding, or request syntax errors. For example, if a support portal rejects 8.4 MB screenshots after a proxy was set to 5 MB, that should be a 413, not a 400. In one real-world style case, fixing the status code cut failed upload tickets by about 32% because users finally saw a clear “file too large” message instead of a vague “bad request.”
Why 413 and 400 get mixed up
The confusion comes from the same ugly symptom: the request fails before the app finishes handling it. A user clicks Upload, waits two seconds longer than usual, and gets an error. To them, it is just broken. To engineers, the exact status code matters.
HTTP 413 means the request is understandable, but too big. The server, proxy, gateway, CDN, or app framework has a size rule and the request crossed it.
HTTP 400 means the request is bad in a more basic way. The server cannot parse it safely or correctly. The body might be invalid JSON. The headers might be corrupt. The declared content length might not match the payload. The request may violate protocol rules.
Honestly, it feels like some systems throw 400 for everything just to avoid thinking. That makes logs cleaner for machines and worse for humans.
The practical difference
Think of the server as a receptionist.
- 413: “Your package is labeled correctly, but it is too heavy for this desk.”
- 400: “I cannot read the label, so I do not know what this package is.”
This distinction affects what the client should do next.
- With 413, the client can shrink the payload, compress it, split it into chunks, or ask for a higher limit.
- With 400, the client should inspect syntax, encoding, headers, and serialization.
A retry usually will not fix either code by itself. But the fix is different. Retrying a 20 MB upload against a 10 MB limit just wastes bandwidth. Retrying malformed JSON wastes time and clutters logs.
Common causes of HTTP 413
Oversized requests appear in more places than simple file uploads. Watch for these frequent triggers:
- Large multipart forms: Profile photos, PDFs, videos, log bundles, and invoices.
- Base64 encoded files: Base64 can increase size by roughly one third, so a 7.5 MB image may become about 10 MB in JSON.
- Huge JSON payloads: Bulk imports, analytics events, product catalogs, and nested arrays.
- Verbose metadata: Mobile apps sometimes attach device logs, timestamps, screen state, and trace IDs to every request.
- Proxy limits: Nginx, Apache, API gateways, CDNs, and load balancers can reject the request before your application sees it.
Nginx, for example, uses client_max_body_size to control request body size, and a mismatch between Nginx and the app limit is a classic source of confusion [source: NGINX].
Common causes of HTTP 400
HTTP 400 is broader. That makes it useful, but also easy to misuse.
- Invalid JSON: Missing quotes, trailing commas, broken escaping, or partial bodies.
- Wrong content type: Sending JSON while declaring
text/plain, or sending form data asapplication/json. - Bad headers: Illegal characters, oversized header fields, duplicate fields that your stack rejects.
- Encoding issues: UTF-8 errors, gzip problems, or incorrect charset declarations.
- Content length mismatch: The header says one size, but the body sends another.
If the payload is 30 KB and still fails, 413 is probably not your culprit. Start with parsing, headers, and request construction.
How to troubleshoot 413 without guessing
Start at the edge and move inward. The request may never reach your application.
- Check the actual request size. Measure the full body, not just the original file. Multipart boundaries, base64, and metadata add bytes.
- Check CDN and WAF limits. Some hosted layers have hard caps. Your app setting may be irrelevant.
- Check reverse proxy limits. Nginx, Apache, Envoy, HAProxy, and cloud load balancers can stop large bodies early.
- Check app server limits. Node, Express, Django, Rails, Spring, Laravel, ASP.NET, and other frameworks often have body parser limits.
- Check endpoint-specific limits. A profile photo endpoint may allow 2 MB while an import endpoint allows 50 MB.
- Return a helpful response. Tell the client the maximum size when safe: “Maximum upload size is 10 MB.”
The annoying part is that every layer may have a different default. One team raises the app limit to 25 MB, but the proxy stays at 10 MB. Users still fail. Everyone stares at the wrong logs.
How to troubleshoot 400 when size looks suspicious
Sometimes a large request gets a 400, and the team assumes it should be a 413. Not always. A big payload can also be malformed.
Use this checklist:
- Validate the body locally. Parse the exact payload that failed, not a recreated sample.
- Compare headers with body. Confirm
Content-Type,Content-Length, and compression settings. - Inspect truncation. If a client times out mid-upload, the server may receive a partial body and return 400.
- Check encoding. Binary data shoved into JSON without proper encoding can break parsing.
- Capture raw requests. Application logs often hide the real protocol problem.
If the server understood the request and rejected only the size, change the response to 413. If parsing failed before size was the issue, keep 400.
What clients should do with each code
Good clients react differently to 413 and 400.
- For 413: Show a clear size message. Offer compression, cropping, chunked upload, or a lower-quality export.
- For 400: Do not tell users to shrink files unless size is proven. Log a request ID and ask the client app to fix formatting.
- For APIs: Return machine-readable error details, such as
maxBytes,actualBytes, orreason.
A clean 413 response might say:
{
"error": "content_too_large",
"message": "Maximum request size is 10485760 bytes.",
"maxBytes": 10485760
}
A clean 400 response might say:
{
"error": "invalid_json",
"message": "Request body contains malformed JSON."
}
Best practice: be specific, not lazy
Use 413 when the request is valid enough to measure and reject by size. Use 400 when the request is syntactically invalid or cannot be parsed. That rule is simple, but it pays off fast.
Clear status codes improve dashboards, alerts, documentation, and user support. They also stop backend teams from chasing phantom parser bugs when the real issue is a 5 MB gateway cap. Set consistent limits across every layer, document them, and test them with payloads just below and just above the limit.
The best error is not only technically correct. It tells the next person what to fix.