"413 Request Entity Too Large" from nginx means the request body was bigger than client_max_body_size, which is 1 MB unless you set it. Add client_max_body_size 64m; to the server block of your site (or the http block for every site), run nginx -t, reload nginx, and the error is gone. If uploads still fail after that, the limit is somewhere else: PHP's upload_max_filesize and post_max_size, a second nginx inside a Docker container or Kubernetes ingress, or Cloudflare, which stops anything above 100 MB on the Free and Pro plans.
That is the short version. The longer one matters because the 413 is often only the first wall. On 24 October 2026 we ran a separate nginx 1.26.3 and PHP-FPM 8.4.27 instance on a Debian 13 test VPS and pushed files of 900 KB, 5 MB, 50 MB and 70 MB through it with curl, before and after each change. Two results surprised people we showed them to: a file of exactly 1 MB is refused by the default limit, and once nginx lets a big file through, PHP rejects it with an HTTP 200 and no error page at all.
The same fix works on any nginx server, including an RS Computers VPS or VDS in Amsterdam, Dublin or Prishtina, where you have root and edit the configuration yourself.
Fix 413 Request Entity Too Large in nginx, step by step
Run these as root, or put sudo in front. The paths are the Debian and Ubuntu ones; on AlmaLinux and Rocky the site files live in /etc/nginx/conf.d/.
1. See whether a limit is already set anywhere. nginx -T prints the whole loaded configuration, including every included file:
nginx -T 2>/dev/null | grep -n client_max_body_size
No output means nginx is on its 1 MB default. Debian 13's stock /etc/nginx/nginx.conf does not set it. If you do get lines, note which block each one is in; a value in a location beats the one in server, which beats http.
2. Open your site's file, for example /etc/nginx/sites-available/example.com, and add one line inside the server { } block, next to server_name:
server {
server_name example.com;
client_max_body_size 64m;
...
}
Write the unit as k, m or g. We tried 64MB and nginx refused to load:
[emerg] "client_max_body_size" directive invalid value in /root/lab-nginx-413-request-entity-too-large-fix/conf/nginx.conf:18
nginx: configuration file /root/lab-nginx-413-request-entity-too-large-fix/conf/nginx.conf test failed
3. Test, then reload. A reload keeps open connections alive; nothing goes down.
nginx -t
systemctl reload nginx
You want to see:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
If the test fails, do not reload; the running nginx keeps the old, working configuration until you fix the line it names.
4. Prove it with curl (see testing with curl below) instead of with the browser, so you see the real status code.
Where the 1 MB default comes from, and how scope works
The nginx documentation gives client_max_body_size 1m as the default, valid in the http, server and location contexts. When a request is larger, "the 413 (Request Entity Too Large) error is returned to the client", and a value of 0 switches the check off (nginx core module docs).
The limit counts the whole request body, not the file. A browser or curl form upload wraps the file in multipart headers, a couple of hundred bytes in our tests. With the limit at 1m we got:
| What we sent | Body size | Result |
|---|---|---|
| Raw body of 1,048,576 bytes | exactly 1 MiB | 200 |
| Raw body of 1,048,577 bytes | 1 byte over | 413 |
| Form upload of a 1,000,000-byte file | under 1 MiB with headers | 200 |
| Form upload of a 1,048,576-byte file | over 1 MiB with headers | 413 |
So give yourself headroom: if users upload 50 MB files, set 64m, not 50m.
The mistake we see most: the limit in the wrong location
nginx picks one location per request, and only that block's settings apply. A very common WordPress or Laravel configuration has location / for files and location ~ \.php$ for PHP. Put the limit in location / and uploads to upload.php never see it. We tested exactly that:
location / {
client_max_body_size 64m;
}
location ~ \.php$ {
include fastcgi_params;
...
}
A 5 MB upload to /upload.php still got 413, and nginx -t said nothing, because the configuration is valid. Put it in server instead, and lower it per path where you want a tighter rule:
server {
client_max_body_size 64m;
location = /avatar.php {
client_max_body_size 2m;
...
}
}
With that, a 5 MB file went through to /upload.php (200) and was refused at /avatar.php (413), while a 900 KB avatar still passed. Keeping small limits on forms that never need big files is a cheap way to stop someone filling your disk.
What our test server showed, before and after
Setup: Debian 13, nginx 1.26.3 and PHP-FPM 8.4.27 from the Debian packages, run as a private instance on port 18082 next to the system's own services. Files were random data. PHP started with Debian's production values, upload_max_filesize = 2M and post_max_size = 8M.
| Setting | 900 KB | 5 MB | 50 MB | 70 MB |
|---|---|---|---|---|
| nginx default (1m), PHP 2M / 8M | 200, saved | 413 from nginx | 413 from nginx | 413 from nginx |
| nginx 64m, PHP 2M / 8M | 200, saved | 200, but PHP error 1, file dropped | 200, but $_FILES empty | 413 from nginx |
| nginx 64m, PHP 64M / 64M | 200, saved | 200, saved | 200, saved | 413 from nginx |
| nginx 0 (no limit), PHP 64M / 64M | 200, saved | 200, saved | 200, saved | 200, but $_FILES empty |
Row two is the trap. Fixing nginx alone made the 413 disappear, and the upload still failed, silently. A 70 MB file with nginx unlimited shows the same thing one level up: PHP becomes the real limit and it does not say so in the browser.
When nginx rejects a body, its error log says why. This is the line from our 5 MB test:
[error] 34834#34834: *2 client intended to send too large body: 5243083 bytes, client: 127.0.0.1, server: lab.local, request: "POST /upload.php HTTP/1.1", host: "127.0.0.1:18082"
On Debian, look in /var/log/nginx/error.log:
grep "too large body" /var/log/nginx/error.log | tail -n 5
Uploads sent in chunks, without a Content-Length header, are caught too. nginx counts as it reads and logs a slightly different line, which we saw when we forced chunked encoding:
client intended to send too large chunked body: 1048384+65524 bytes
PHP: upload_max_filesize and post_max_size
PHP has two limits of its own and both must be at least as big as the file:
upload_max_filesizeis the largest single file. A file over it arrives with error code 1,UPLOAD_ERR_INI_SIZE, and size 0 (PHP upload errors).post_max_sizeis the whole request. Over it, PHP throws the body away: "the $_POST and $_FILES superglobals are empty" (PHP core settings). Your app may then report a vague "no file selected" or nothing.
The only trace is a warning in the PHP log. Ours read:
PHP Warning: PHP Request Startup: POST Content-Length of 52429018 bytes exceeds the limit of 8388608 bytes in Unknown on line 0
To raise them on Debian or Ubuntu with PHP 8.4, edit the FPM file, not the CLI one:
nano /etc/php/8.4/fpm/php.ini
Find and change the two lines (Ctrl+W searches in nano):
upload_max_filesize = 64M
post_max_size = 64M
Then restart PHP-FPM and check what the FPM side really loaded:
systemctl restart php8.4-fpm
php-fpm8.4 -i | grep -E "^(upload_max_filesize|post_max_size)"
Expected:
post_max_size => 64M => 64M
upload_max_filesize => 64M => 64M
Do not trust php -i for this. It reads /etc/php/8.4/cli/php.ini, the command-line copy, so it can show 64M while the website still runs on 2M.
Several sites on one server with different needs? Set it per FPM pool instead, in /etc/php/8.4/fpm/pool.d/www.conf or the site's own pool file:
php_admin_value[upload_max_filesize] = 32M
php_admin_value[post_max_size] = 32M
In our test the pool value won over a 64M php.ini: the script reported 32M, a 5 MB file was saved and a 50 MB one arrived with $_FILES empty.
WordPress media uploads
WordPress shows "Maximum upload file size" on the Media, Add New screen. That number comes from PHP: wp_max_upload_size() takes the smaller of upload_max_filesize and post_max_size (WordPress developer reference). It knows nothing about nginx. So a site can say "64 MB" there and still fail on a 5 MB photo with a short "HTTP error" in the media library, because nginx was left at 1 MB. That error message is the browser-side version of the 413; nginx's docs warn that "browsers cannot correctly display this error". Set nginx and PHP to the same value and the number WordPress shows becomes true.
The 64m line already sits in the server blocks of our guide to hosting WordPress on a VPS and the WordPress migration walkthrough, because importing a backup plugin's archive is where most people first meet this error.
Two nginx in a row: Docker, Nginx Proxy Manager, Kubernetes
When a request passes through a reverse proxy, every hop has its own limit, and the smallest one wins. We rebuilt that on the test server: a front nginx with 64m passing /app/ to a second nginx left on the default, the way an app container often ships.
A 900 KB upload went through. A 5 MB one got 413, although the front proxy allowed 64 MB. The error log showed who refused it:
[error] 41287#41287: *40 client intended to send too large body: 5243083 bytes, client: unix:, server: , request: "POST /upload.php HTTP/1.0", host: "localhost"
The clues are HTTP/1.0 (nginx talks HTTP/1.0 to upstreams by default) and a client that is the proxy, not a visitor. Inside Docker the client would be the proxy container's IP, such as 172.18.0.2. Once we added client_max_body_size 64m; to the inner server too, the same 5 MB upload returned 200 and was saved.
Where to set it in common setups:
- Nginx Proxy Manager. Its main
nginx.confalready setsclient_max_body_size 2000m;in thehttpblock (source on GitHub), so a 413 behind it usually comes from the app container or from Cloudflare. To set a value per host, edit the proxy host, open the Advanced tab and putclient_max_body_size 0;or a size in the custom configuration box. - An app container with its own nginx (Nextcloud, Immich and many PHP images). Raise it in that image's config or its documented environment variable, and also in the host proxy. Our reverse proxy and free SSL guide covers the outer layer; the Docker on a VPS guide the containers behind it.
- Kubernetes with ingress-nginx. Use the annotation
nginx.ingress.kubernetes.io/proxy-body-size: 64mon the Ingress, or set it for all of them in the controller's ConfigMap (ingress-nginx annotations). Know that the Kubernetes project retired ingress-nginx in March 2026, with no further releases or security fixes (Kubernetes blog); if you are fixing this on a cluster, it is a good moment to plan the move to a Gateway API controller. - Caddy or Traefik in front do not cap the body by default, so with them the limit is nearly always the nginx or PHP behind.
Cloudflare: 100 MB on Free and Pro
If your domain is proxied through Cloudflare (orange cloud), uploads are limited before they reach your server: 100 MB per request on Free and Pro, 200 MB on Business, up to 5 GB on Enterprise (Cloudflare error 413). The 413 page then comes from Cloudflare, and nothing appears in your nginx log, which is the quickest way to tell. Raising nginx past 100 MB changes nothing here. The options are an upload subdomain set to DNS only (grey cloud), splitting files into chunks in the app (many upload libraries and Nextcloud already do), or a bigger plan.
How to test with curl
A browser hides the real answer. curl shows it. Make a test file of a size you choose on your own computer or the server (Linux and macOS):
head -c 5M /dev/urandom > test-5mb.bin
Send it as a form upload and print only the status code. Replace the URL with your upload endpoint:
curl -s -o /dev/null -w "%{http_code}\n" -F "file=@test-5mb.bin" https://example.com/upload.php
413 means a proxy or nginx refused it; 200 means the body got through, but check the app or the PHP log to be sure it was kept. To see the headers of the exchange:
curl -sv -o /dev/null -F "file=@test-5mb.bin" https://example.com/upload.php 2>&1 | grep -E "^[<>] (POST|HTTP|Content-Length|Expect|Server)"
Before the fix, ours printed:
> POST /upload.php HTTP/1.1
> Content-Length: 5243083
> Expect: 100-continue
< HTTP/1.1 413 Request Entity Too Large
< Server: nginx/1.26.3
< Content-Length: 183
curl asks first with Expect: 100-continue, so nginx answered 413 without receiving the 5 MB at all. A Server: cloudflare line instead of nginx tells you the edge refused it. If the endpoint needs a login, test against a simple PHP script that only prints $_FILES, like the one we used, put in a private path and removed afterwards.
Frequently asked questions
What does 413 Request Entity Too Large mean in nginx?
It means the request body, usually an upload or a large form or API post, was bigger than the client_max_body_size limit, which is 1 MB by default. nginx refuses it before your application sees it. In newer HTTP specifications the same status is called "413 Content Too Large".
Where do I put client_max_body_size?
In the http block for every site, in a server block for one site, or in a location block for one path. The most specific one that matches the request wins. Putting it in location / does not cover PHP files handled by a separate location ~ \.php$ block.
Do I need to restart nginx after changing client_max_body_size?
No, a reload is enough: nginx -t and then systemctl reload nginx. Existing connections are not dropped.
Is client_max_body_size 0 safe?
It turns the size check off. Anyone can then send a request as large as your disk will hold to any path, and nginx buffers big bodies to a temporary file first. Prefer a real number a bit above your largest expected upload, with lower values on paths that never need large files.
I fixed nginx and the upload still fails. Why?
Usually PHP: upload_max_filesize or post_max_size is smaller than the file. PHP then answers with HTTP 200 and drops the file, so there is no 413 to see. Check php-fpm8.4 -i (not php -i) and the PHP error log. If nothing shows on the server at all, look at Cloudflare or another proxy in front.
What is the Cloudflare upload limit?
100 MB per request on the Free and Pro plans, 200 MB on Business and up to 5 GB on Enterprise, according to Cloudflare's documentation.
A server where you set the limits
On shared hosting the upload limit is often whatever the provider picked. On a VPS you edit the nginx and PHP files yourself, as above, and nothing in between overrides them. RS Computers runs KVM VPS and VDS plans in Amsterdam, Dublin and Prishtina on NVMe storage with full root access; for a WordPress site with big media libraries, see the WordPress VPS hosting plans. If an upload still fails after these steps, send us the curl output on Telegram and we will look at it with you.