How to fix "413 Request Entity Too Large" and raise the upload limit for PHP on Nginx
An upload can be stopped in three places: by Nginx, by PHP's file limit and by PHP's form limit. Only one of them shows an error page. This guide shows how to find which one stopped it, and how to raise it in the right place.
8.4 to your PHP version.The three limits
These are the defaults on a fresh Debian or Ubuntu server:
- Nginx
client_max_body_size: 1 MB. Anything bigger gets a 413 Request Entity Too Large page from Nginx. PHP never sees it. - PHP
upload_max_filesize: 2 MB. The biggest single file PHP accepts. A bigger file is dropped without an error page and without a log line. Your app just sees an upload that failed. - PHP
post_max_size: 8 MB. The biggest whole form, with all files in it. If the form is bigger, PHP throws away everything: the files and the other fields. The form looks like it was sent empty.
So a 5 MB photo gets a 413 page first. Once you fix Nginx, it fails again, but now quietly. That's why people often think their fix "didn't work". You need to raise all three.
1. Find out which limit stopped the upload
Try the upload once, then look at the Nginx error log:
sudo tail -n 20 /var/log/nginx/error.log
client intended to send too large body: 5243087 bytes: Nginx stopped it. See step 2.PHP Warning: PHP Request Startup: POST Content-Length of 12583120 bytes exceeds the limit of 8388608 bytes: the form was bigger thanpost_max_size. See step 3.- Nothing in the log, and the upload still failed: the file was bigger than
upload_max_filesize. PHP marks the file with error code 1 (UPLOAD_ERR_INI_SIZE) and logs nothing. See step 3.
In WordPress, Media → Add New shows "Maximum upload file size". It shows the smaller of the two PHP limits, so it's a quick way to see if your PHP change worked. It doesn't know about the Nginx limit.
2. Raise the Nginx limit
Debian and Ubuntu don't set client_max_body_size at all, so the 1 MB default applies. Add it to the server block of your site, in /etc/nginx/sites-available/:
server {
server_name example.com;
client_max_body_size 64m;
...
}
Then test and reload:
sudo nginx -t && sudo systemctl reload nginx
Pick a number a bit bigger than the biggest file you really need. Don't use 0. It turns the check off, and then anyone can send your server huge requests.
Only one page needs big uploads? Keep a small limit for the site and raise it for that one URL. In our test, a 5 MB upload worked on /upload.php and got a 413 on every other page:
server {
client_max_body_size 2m;
location = /upload.php {
client_max_body_size 64m;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
}
For WordPress, the media uploader sends files to /wp-admin/async-upload.php.
3. Raise the PHP limits
The web server uses PHP-FPM, which has its own settings, separate from the php command you type in the terminal. Changing /etc/php/8.4/cli/php.ini does nothing for your site. In our test, the website used 64M after the change while php in the terminal still said 2M.
Put your changes in a small file of their own, so they're easy to find later:
sudo nano /etc/php/8.4/fpm/conf.d/99-uploads.ini
upload_max_filesize = 64M
post_max_size = 64M
Make post_max_size at least as big as upload_max_filesize. If people upload several files at once, make it bigger still, because it covers the whole form.
Then check and reload PHP-FPM. A reload was enough in our test:
sudo php-fpm8.4 -t && sudo systemctl reload php8.4-fpm
Check the values PHP-FPM really uses (not the terminal ones):
php-fpm8.4 -i | grep -E '^(upload_max_filesize|post_max_size)'
You should see upload_max_filesize => 64M => 64M. If not, another file in conf.d may set it again. Files are read in name order, so the last one wins. That's why ours starts with 99-.
You don't need a bigger memory_limit. PHP writes uploads to a temporary file, not to memory. In our test, a 40 MB upload worked fine with memory_limit = 32M. Only raise it if your app loads the whole file into memory, for example to resize a big image.
One more limit: max_file_uploads is 20 by default. That's the most files one form can send at once.
Different limits for each site
If each site has its own PHP-FPM pool, set the limits in that site's pool file instead, for example /etc/php/8.4/fpm/pool.d/shop.conf:
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
Then sudo php-fpm8.4 -t && sudo systemctl reload php8.4-fpm. Values set with php_admin_value can't be changed by the site's own files. In our test, a .user.ini that tried to set 2M was ignored.
No root access? Use .user.ini
If you can only change files in the website's folder, put a file named .user.ini in the site's main folder (where index.php is):
upload_max_filesize = 64M
post_max_size = 64M
This works with PHP-FPM. Two things to know from our test:
- It can take up to 5 minutes. PHP reads the file again only every 300 seconds. A reload of PHP-FPM makes it work right away.
ini_set()in your code doesn't work for these two settings. PHP has already read the upload by the time your code runs. Our test page still said 2M afterini_set('upload_max_filesize', '64M').
A .htaccess file with php_value lines does nothing on Nginx.
On Apache
Apache doesn't have Nginx's 1 MB limit. In our test (Apache 2.4 with PHP-FPM), a 70 MB upload went through Apache to PHP, and only PHP's limits stopped it. So on Apache, step 3 is usually all you need.
Behind Cloudflare
If your site goes through Cloudflare's proxy, Cloudflare has its own limit. On the Free and Pro plans it's 100 MB per request, and bigger uploads get a 413 from Cloudflare, before they reach your server. For bigger files, upload to a subdomain that isn't proxied, or split the file into parts in the browser.
Keep it safe
- Don't raise limits more than you need. Big limits let one visitor fill your disk or keep PHP workers busy.
- Check what people upload. A bigger limit is also a bigger door for unwanted files. Never let uploads run as PHP: see hardening WordPress for how to block PHP in the uploads folder, and scanning uploads with ClamAV.
- Watch the disk. Big uploads add up. See stop logs filling the disk for how to find what's using space.
Quick list
- 413 page from Nginx: add
client_max_body_size 64m;to the site'sserverblock, thennginx -tand reload. - Upload fails quietly: raise
upload_max_filesizein/etc/php/8.4/fpm/conf.d/99-uploads.ini. - Form comes back empty: raise
post_max_sizein the same file. - Changed php.ini but nothing happened: you probably changed the CLI one. Check with
php-fpm8.4 -i. - .user.ini change not working: wait 5 minutes or reload PHP-FPM.
- Over 100 MB behind Cloudflare: Cloudflare's own limit. Use a subdomain that isn't proxied.
Official sources
- Nginx: client_max_body_size
- PHP core settings (php.net) (
upload_max_filesize,post_max_size,max_file_uploads) - PHP upload error codes (php.net)
- .user.ini files (php.net)
- Cloudflare: 413 and upload limits per plan
Next steps: if PHP shows a 502 or 504 instead, see fixing 502 and 504 errors. New server? Start with the security checklist for a PHP server.
ServerNest is being built to let you change PHP settings like these for each site from one simple dashboard. Join early access to try it first.