Skip to main content
DevOps Insightsguides & answers

nginx · troubleshooting

413 Request Entity Too Large

Something between the client and your application refused the request because its body was larger than that layer allows. On nginx the limit is client_max_body_size, 1m unless set. In Kubernetes ingress-nginx it is the proxy-body-size annotation, also 1m by default. The application itself or a CDN in front can send the same status, and the response body tells you which.

Reproduced on this site's nginx, . Where the text says “reproduced”, the behaviour was run on that version, not taken from documentation.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 10 sections

Before you start

  • A way to repeat the failing request, such as curl with a file of the same size.
  • Read access to the nginx configuration and error log on the server, or kubectl access to the Ingress and the ingress-nginx ConfigMap.
  • Permission to reload nginx or edit the Ingress, since the fix is a configuration change.

Run this first

step 1 of 3
curl -sS -X POST --data-binary @body.bin -w '\nstatus=%{http_code} sent=%{size_upload}\n' https://<host>/<path>

Look for nginx's HTML page, the application's own format, or a CDN's page; a small sent value means the limit was enforced from Content-Length

Repeats the failing request with a body of the same size, and shows who answered and how much was sent first.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

What does the 413 response body look like?

Repeat the request with curl and read the body

Cause space

4 of 4 still possible

  • nginx's client_max_body_size is smaller than the bodyCommon
  • In Kubernetes, the ingress-nginx proxy-body-size is smaller than the bodyCommon
  • The application behind nginx has its own body limitOccasional
  • A CDN or proxy in front of nginx has a lower limitRare

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Check it with a tool

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    curl -sS -X POST --data-binary @body.bin -w '\nstatus=%{http_code} sent=%{size_upload}\n' https://<host>/<path>

    Repeats the failing request with a body of the same size, and shows who answered and how much was sent first.

    Look for nginx's HTML page, the application's own format, or a CDN's page; a small sent value means the limit was enforced from Content-Length

  2. Step 2

    sudo grep 'too large body' /var/log/nginx/error.log | tail -5

    nginx logs every body it rejects for size, with the size.

    Look for client intended to send too large body: <bytes> bytes

  3. Step 3

    sudo nginx -T 2>/dev/null | grep -n -B3 client_max_body_size

    Prints the full loaded configuration, so you can see which block sets the limit for your path.

    Look for The value in the block that serves the path, or no setting, meaning 1m

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

nginx's client_max_body_size is smaller than the body

Common

No limit was set, so the 1m default applies, or a limit was set somewhere less specific than the location the upload goes to. The most specific block wins: a location's own setting overrides the server's, which overrides http's.

Confirm

sudo nginx -T 2>/dev/null | grep -n client_max_body_size
sudo grep 'too large body' /var/log/nginx/error.log | tail -5

No setting at all, meaning 1m, or a value below the body size in the block that serves the path, and log lines giving the rejected size

Fix

  • Set client_max_body_size in the location that receives uploads, to the largest size you intend to accept.
  • Test the configuration and reload, then repeat the failing request.
# in the server or location block that serves the upload path:
#   client_max_body_size 20m;
sudo nginx -t && sudo systemctl reload nginx

nginx -t checks the configuration before the reload, so a typo does not take the site down.

In Kubernetes, the ingress-nginx proxy-body-size is smaller than the body

Common

ingress-nginx writes client_max_body_size from the proxy-body-size annotation on the Ingress, or from the controller ConfigMap's proxy-body-size key, and the ConfigMap default is 1m. Editing nginx.conf inside the controller pod does not last; the controller regenerates it.

Confirm

kubectl get ingress <name> -n <namespace> -o jsonpath='{.metadata.annotations}'

No nginx.ingress.kubernetes.io/proxy-body-size annotation, or one smaller than the body

Fix

  • Add the annotation to the Ingress that serves the upload path.
  • Use the ConfigMap key only if every Ingress really needs the larger limit.
kubectl annotate ingress <name> -n <namespace> nginx.ingress.kubernetes.io/proxy-body-size=20m --overwrite

The annotation and the ConfigMap key are from the ingress-nginx documentation. Other ingress controllers have their own settings.

The application behind nginx has its own body limit

Occasional

nginx accepted the body and passed it on, and the application's body parser refused it. Express's body-parser defaults to 100kb and answers 413 with type entity.too.large. nginx then relays the application's 413.

Confirm

curl -sS -X POST --data-binary @body.bin -o - http://127.0.0.1:<app-port>/<path>

Run on the server, straight to the application. A 413 here, in the application's own format rather than nginx's page, means the application is the limit

Fix

  • Raise the limit for the route that needs it, for example express.json({ limit: '5mb' }) on that route only.
  • Keep nginx's limit at or slightly above the application's, so oversized requests are refused before they reach it.

A CDN or proxy in front of nginx has a lower limit

Rare

Another proxy refuses the body before it reaches your server. Cloudflare documents 100 MB for the Free and Pro plans and 200 MB for Business, and returns 413 above that. nginx never sees the request.

Confirm

curl -sS -D - -o /dev/null -X POST --data-binary @body.bin https://<host>/<path> | grep -iE '^(HTTP|server|via|cf-ray)'

A server or via header naming the CDN, and no matching 413 in nginx's access log

Fix

  • Upload large files directly to object storage with a pre-signed URL, bypassing the proxy, or raise the limit where the plan allows it.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

A server can cap how much data it accepts in one request, for example in a file upload. When a request is bigger than the cap, the server answers 413 and never passes the data on. nginx's cap is 1 megabyte unless someone raised it. The fix is to raise the cap in the right place, but only as far as you actually need.

A 6 MB upload against a 5 MB limit

Reproduced against this site's own nginx, which sets client_max_body_size 5m, with curl 8.7.1 on macOS. The body was 6 MiB of zeros made with head -c 6291456 /dev/zero > body.bin. A 4 MiB body to the same URL returned 200.

Command

curl -sS -X POST --data-binary @body.bin -w '\nstatus=%{http_code} sent=%{size_upload}\n' https://<host>/

Output

<html>
<head><title>413 Request Entity Too Large</title></head>
<body>
<center><h1>413 Request Entity Too Large</h1></center>
<hr><center>nginx</center>
</body>
</html>

status=413 sent=65383

How to read it

  • The HTML page with nginx at the bottom is nginx's built-in error page. An application or a CDN rejecting the body answers in its own format, so this page alone says nginx is the layer to change.
  • sent=65383 is the point. curl uploaded only about 64 KB of the 6 MiB before nginx answered: nginx compares the declared Content-Length with the limit and rejects the request without reading the body.
  • On the server, nginx's error log records the same refusal as client intended to send too large body: followed by the size in bytes.

Words used on this page

client_max_body_size
The nginx directive that sets the largest request body nginx accepts. The default is 1m; 0 turns the check off. It can be set in the http, server and location blocks, and the most specific one applies.
Request body
The data sent after the headers, such as an uploaded file or a JSON payload. GET requests usually have none.
Content-Length
A request header giving the body's size in bytes. nginx compares it with the limit before reading the body.
proxy-body-size
The ingress-nginx setting that becomes client_max_body_size: an annotation on one Ingress, or a key in the controller's ConfigMap for all of them. The default is 1m.
413 Content Too Large
The status's current name in the HTTP standard, RFC 9110. nginx still prints the older name, Request Entity Too Large.
What is actually happening

413 means the server refuses to process the request because its content is larger than it is willing or able to handle. The status is defined in RFC 9110, which calls it Content Too Large; nginx and many tools still print the older name, Request Entity Too Large.

nginx returns it when a request's body exceeds client_max_body_size, which defaults to 1m. nginx's own documentation warns that browsers cannot correctly display this error, so from the browser it often looks like a failed or stalled upload rather than a clear message.

When the client declares the size with Content-Length, nginx checks it before reading the body, logs client intended to send too large body: <bytes> bytes, and answers at once. That is why the reproduction above sent only 64 KB. Chunked uploads without a length are counted as they arrive and logged as too large chunked body.

nginx is not the only layer that sends 413. An application's body parser has its own limit, Express's is 100kb by default, and so do CDNs: Cloudflare's is 100 MB on the Free and Pro plans. Each layer has to allow the size, and the first one that does not is the one that answers.

How to tell this is your problem
WhereWhat you see
The response body<center>413 Request Entity Too Large</center> … <center>nginx</center>
nginx error logclient intended to send too large body: <bytes> bytes
nginx access logA POST or PUT with status 413
Browser, during an uploadThe upload fails or stalls with no readable error page
An Express applicationAn error with status 413 and type entity.too.large

How to know it is actually fixed

  • The original request, or a curl with a body of the same size, returns the application's normal response instead of 413.
  • A body above the new limit still gets 413, which shows the limit is set where you meant it to be rather than switched off.
  • No new client intended to send too large body lines appear in the error log for legitimate uploads.

Stopping it happening again

  • Decide the largest upload each endpoint accepts, and set the same number in nginx, the ingress and the application, documented in one place.
  • Make the upload form check the file size in the browser first and show a clear message, since browsers do not display nginx's 413 page well.
  • For files much larger than a few megabytes, upload directly to object storage with pre-signed URLs instead of through your web server.

Security considerations

  • The limit is a protection, not an obstacle. It bounds how much an anonymous client can make nginx buffer, so raise it only in the location that receives uploads and only to the size you need, rather than setting 0 or a large value server-wide.
  • Raising nginx's limit moves the load to the application. Make sure the upload handler streams to disk or object storage and enforces its own maximum, instead of reading the whole body into memory.
  • Keep authentication and rate limits in front of any location you open up for large bodies, so the larger allowance is only available to people allowed to upload.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it