Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Troubleshooting

413 Error: Request Entity Too Large, and the Three Limits Behind It

Sean

Platform Writer

Sep 01, 2026
7 min read

A 413 means something in the request path decided the body was too large and refused it before your application ran. The frustrating part is that at least three separate limits can produce it, and raising the wrong one changes nothing.

413 Error: Request Entity Too Large, and the Three Limits Behind It

The layers are the reverse proxy, the runtime, and the application framework. Each has its own ceiling, each defaults to something modest, and a request has to pass all three. People raise the one they read about first, restart, see the same error, and conclude the change did not apply.

It did apply. There was another limit behind it.

Table of contents

Finding which layer refused

Before changing anything, work out which layer produced the error, because the response tells you more than it appears to.

A 413 from a reverse proxy typically arrives fast, with the proxy’s own branded error page, and nothing in your application log. A 413 from the framework arrives after the body has been read, is usually formatted like your other error responses, and does appear in your log.

Check the access log first:

sudo grep ' 413 ' /var/log/nginx/access.log | tail -10
sudo tail -50 /var/log/nginx/error.log | grep -i 'client intended to send too large body'

That error log line is definitive. If it says the client intended to send too large a body, the proxy refused it and the limit to raise is the proxy’s.

If the access log records a 413 with no such error line, or records a 200 with your application returning the error, the refusal is further in. If nothing appears at all, something above your server refused it, which on a CDN-fronted site is a real possibility with its own separate ceiling.

Layer one: the reverse proxy

This is the most common culprit because the default is low: 1MB on nginx, which is smaller than a phone photograph.

# In /etc/nginx/nginx.conf inside the http block, to apply everywhere:
http {
    client_max_body_size 50M;
}

# Or scoped to one location, which is better practice:
location /api/upload {
    client_max_body_size 200M;
    proxy_pass http://127.0.0.1:3000;
}

Scoping it to the upload endpoint rather than raising it globally is worth the extra two lines. A global 200MB limit means every endpoint on the site will accept a 200MB body, which is a denial-of-service surface you did not need to create.

sudo nginx -t && sudo systemctl reload nginx

Always run the config test before reloading. A syntax error plus a reload is a brief outage; a syntax error caught by the test is nothing.

Two related settings sometimes matter alongside it. client_body_timeout governs how long the client has to send the body, and a large upload on a slow connection can hit it. client_body_buffer_size decides when the body is buffered to disk rather than memory, which affects performance rather than acceptance.

Layer two: the runtime

If your application runs under PHP, there are two more ceilings and they interact.

; In php.ini
upload_max_filesize = 50M
post_max_size = 52M
memory_limit = 128M
max_execution_time = 300

The relationship that catches people: post_max_size must be larger than upload_max_filesize, because the POST body contains the file plus the other form fields plus the multipart encoding overhead. Setting them equal means a file at exactly the upload limit fails on the post limit.

Confirm what is actually in effect rather than what you edited, since there are usually several ini files and one may be overriding another:

php -i | grep -E 'upload_max_filesize|post_max_size|memory_limit'
php --ini

Then restart the process manager, not just the web server. Editing php.ini and reloading nginx changes nothing, because the values live in the PHP-FPM workers.

sudo systemctl restart php8.3-fpm

For Node, Python and Ruby applications there is usually no runtime-level limit of this kind, and the framework limit in the next section is the second ceiling instead.

Layer three: the framework

Most web frameworks impose their own body size limit, and the defaults are conservative. This is the layer people forget entirely, having already raised the other two.

// Express: the default JSON limit is 100kb.
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ limit: '10mb', extended: true }));

Django has DATA_UPLOAD_MAX_MEMORY_SIZE and FILE_UPLOAD_MAX_MEMORY_SIZE. Rails has a limit in Rack. Spring has multipart properties. The names differ; the pattern does not.

The rule for getting this right first time: set each layer slightly larger than the one in front of it, so the outermost limit is the one that fires. If your proxy allows 50MB, your runtime allows 55MB and your framework allows 60MB, then a 60MB upload is refused by the proxy with a clean 413 rather than by the framework after transferring the entire body.

That ordering matters for more than tidiness. Refusing at the outermost layer means the bytes are never transferred, which is faster for the user and cheaper for you.

Why raising the limit is often the wrong fix

Before setting a large ceiling, consider whether the request should be that large at all.

Large uploads through your application server are an awkward pattern. They occupy a worker for the duration of the transfer, consume memory or disk buffering, and scale badly: ten simultaneous 200MB uploads is two gigabytes moving through a process that also has to serve everyone else.

Two better shapes:

  1. Presigned direct upload. Your application issues a short-lived credential and the browser uploads straight to object storage. The bytes never touch your server, the limit stops being yours, and a large upload no longer occupies a worker.
  2. Chunked or resumable upload. The file is split client-side and reassembled. Each request is small, and an interrupted upload resumes rather than restarting, which matters a great deal on mobile connections.

If you do keep uploads flowing through the application, set the limit to the smallest number that serves the real use case, and validate the type and size in the application as well as at the proxy. A limit is not validation.

The storage documentation covers attaching persistent storage to a service on RunxBuild, which is the natural destination once uploads stop living on the application server’s local disk.

How this fits the rest of the stack

Upload handling is one of those features whose cost lands on storage and bandwidth rather than on the application plan, and it is easy to size the compute correctly and be surprised by the rest. The RunxBuild hosting calculator puts storage and egress next to the plan so the arithmetic for a file-heavy feature happens before it ships rather than after the first month of it.

Useful related references:

FAQ

What causes a 413 error?

A request body larger than a configured limit somewhere in the request path. At least three separate limits can trigger it: the reverse proxy, the language runtime, and the application framework. Each has its own default and a request must pass all of them.

How do I fix 413 in nginx?

Set client_max_body_size to a value above your largest legitimate upload, ideally scoped to the specific upload location rather than globally, then test the configuration and reload. If the error persists, the refusal is coming from a layer behind nginx rather than from nginx itself.

Why does the error persist after raising the limit?

Because a different layer is refusing it. Raising the proxy limit does nothing if the framework’s body limit is smaller, and for PHP, editing php.ini requires restarting the process manager rather than just reloading the web server. Check the error log to identify which layer produced the refusal.

What is the difference between upload_max_filesize and post_max_size?

upload_max_filesize caps an individual uploaded file; post_max_size caps the entire POST body, which includes the file plus other form fields plus multipart overhead. post_max_size must be larger, or a file at exactly the upload limit will fail on the post limit.

Should I just set a very large upload limit?

Usually not. Large uploads through your application occupy a worker for the whole transfer and consume memory or disk buffering, which scales badly under concurrency. Presigned direct uploads to object storage or chunked resumable uploads are better shapes, and they move the limit off your server entirely.

#413 error#request entity too large#client max body size#file upload#nginx