VPS Tutorials
Fixing 413 Request Entity Too Large When Uploading Files to a VPS (2026): Nginx, PHP, and Cloudflare Limit Troubleshooting
When a VPS website shows 413 Request Entity Too Large while uploading images, backup archives, or large files, it is usually because the request body size limits of Nginx, PHP-FPM, the application framework, or the CDN are inconsistent. This article troubleshoots client_max_body_size, upload_max_filesize, post_max_size, reverse proxy, and Cloudflare limits layer by layer.

When you upload a 20MB image, a database backup archive, or a WordPress theme, the page suddenly reports:
413 Request Entity Too Large
This error is very common, especially on sites that were just migrated to a VPS, just finished installing Nginx + PHP-FPM, or have Cloudflare in front.
It does not mean "the file is broken," and it does not mean "the disk is full." It means some layer of the service thinks your request is too large and rejects it outright.
The most common layers are four:
- Nginx's
client_max_body_size; - PHP's
upload_max_filesizeandpost_max_size; - The application's own upload limit, such as WordPress, Laravel, or the BT panel;
- CDN / reverse proxy limits, such as Cloudflare.
The key to troubleshooting 413 is: do not change only one place. The lowest limit takes effect.
First use the browser developer tools to look at the response headers, or use curl directly:
curl -I https://example.com/upload
If the response contains:
Server: nginx
it is most likely Nginx blocking it first.
If you have Cloudflare in front, the response headers may show:
server: cloudflare
cf-ray: xxxxx
At this point you need to distinguish whether Cloudflare rejected it or whether Cloudflare forwarded it and the origin rejected it.
Then look at the Nginx logs:
sudo tail -n 80 /var/log/nginx/error.log
A typical log entry is:
client intended to send too large body: 20971520 bytes
Seeing this line makes the direction clear: Nginx's request body size limit is too small.
Nginx's default limit is usually very small; in many environments it is 1MB. That may not even be enough to upload images.
Open the site configuration:
sudo nano /etc/nginx/sites-available/example.com
Or your configuration is at:
/etc/nginx/conf.d/example.conf
Add this inside the server block:
server {
server_name example.com;
client_max_body_size 100M;
root /var/www/example.com;
index index.php index.html;
}
If you only want to raise the limit for a specific upload endpoint, you can also put it inside location:
location /upload/ {
client_max_body_size 100M;
try_files $uri $uri/ /index.php?$query_string;
}
It is not recommended to write 0 to remove the limit entirely. If the upload endpoint has no limit at all, malicious requests can easily fill up the disk, memory, or temporary directory.
After changing it, test first:
sudo nginx -t
If there is no problem, reload:
sudo systemctl reload nginx
If you use OpenResty, BT Nginx, or Nginx Proxy Manager, the essence is the same: find client_max_body_size, only the configuration entry point differs.
Many people change only Nginx, and the 413 disappears, but the upload page then says the file is too large. This is because the request finally reaches PHP, and PHP blocks it again.
First find the PHP configuration file:
php --ini
If you run PHP-FPM, different versions may be at:
/etc/php/8.2/fpm/php.ini
/etc/php/8.1/fpm/php.ini
/etc/php/7.4/fpm/php.ini
Change these values:
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 300
max_input_time = 300
memory_limit = 256M
Note: post_max_size must be greater than or equal to upload_max_filesize.
If you want to upload 100MB files, I usually configure it like this:
upload_max_filesize = 100M
post_max_size = 120M
memory_limit = 256M
After changing it, restart PHP-FPM:
sudo systemctl restart php8.2-fpm
Change the version number according to your machine. You can check it with:
systemctl list-units | grep php | grep fpm
WordPress users are the most likely to get stuck here.
Nginx and PHP have both been changed, but the admin media library still shows a maximum upload of 2MB, 8MB, or 32MB. This means PHP-FPM did not read the configuration you changed, or a panel/plugin is still limiting it.
You can temporarily place a phpinfo.php in the site directory:
<?php phpinfo();
After visiting it, search for:
upload_max_filesize
post_max_size
Loaded Configuration File
Confirm that the page shows the new values.
Remember to delete it after checking:
rm /var/www/example.com/phpinfo.php
For WordPress, also check:
- Whether a security plugin limits uploads;
- Whether the theme or a plugin imposes its own file size limit;
- Whether there is relevant configuration in
wp-config.php; - Whether the PHP configuration in the BT panel overrides your manual changes.
Do not only look at the command-line php --ini; the web site actually uses PHP-FPM, and the two configuration files may not be the same one.
If your architecture is:
用户 -> Nginx 反向代理 -> Docker / Node / PHP / 后端服务
then the Nginx in front must raise its limit, and the service behind it must raise its limit too.
For example, Nginx reverse proxying to Node:
location /api/ {
client_max_body_size 100M;
proxy_pass http://127.0.0.1:3000;
}
Express may also have a body limit:
app.use(express.json({ limit: '100mb' }));
app.use(express.urlencoded({ limit: '100mb', extended: true }));
Laravel / Symfony / Django / Rails each have their own upload limits too. Nginx allowing the request does not mean the application will accept it.
So if the Nginx log has no 413 but the application log has upload errors, go check the application layer.
If the website has Cloudflare in front, do not forget that it has limits too.
Cloudflare's upload size limits differ by plan. The free plan and low-tier plans are usually not suitable for directly uploading very large files.
If you need to upload backup archives of hundreds of MB, it is best not to use ordinary web uploads. More reliable methods are:
- Upload to the VPS with SFTP / SCP;
- Upload directly to object storage;
- Change the backend upload to chunked upload;
- Temporarily pause the proxy and test with a grey-cloud direct connection to the origin.
When testing, you can change the DNS record from orange cloud to grey cloud, bypass Cloudflare, and see whether the 413 disappears.
If it works normally after bypassing, then it is not a problem with the VPS's Nginx or PHP, but a CDN-layer limit.
Many people run WordPress, Nextcloud, or Laravel with Docker, then change /etc/nginx on the host machine, and it has no effect at all.
First confirm where Nginx is:
docker ps
If Nginx is inside a container, the configuration is also inside the container, or mounted out through a volume.
Common paths:
/etc/nginx/conf.d/default.conf
/etc/nginx/nginx.conf
After changing it, reload Nginx inside the container, or restart the container:
docker exec -it nginx nginx -t
docker exec -it nginx nginx -s reload
If you use docker compose:
docker compose restart nginx
Nextcloud also requires checking PHP and the application's own configuration. Changing only the reverse proxy layer is often not enough.
If you only occasionally upload 50MB images, raising the limit to 100M is not a big deal.
But if you frequently upload 1GB or 5GB backup archives, it is not recommended to simply raise the limit very high. The reason is simple:
- Interrupted uploads easily have to start over;
- Nginx temporary files may fill up the disk;
- PHP is prone to timeouts when handling large files;
- The longer the reverse proxy chain, the more failure points there are;
- Malicious requests can more easily saturate bandwidth and disk.
For these scenarios, it is better to use:
scp backup.tar.gz root@server:/data/backups/
Or use rsync:
rsync -avP backup.tar.gz root@server:/data/backups/
If the website backend lets users upload large files, it is recommended to implement chunked upload or direct upload to object storage, rather than letting all large files pass through PHP.
| Symptom | Most likely cause | What to check first |
|---|---|---|
| The page directly shows 413 | Nginx request body limit | client_max_body_size, Nginx error.log |
| 413 disappears but the application says the file is too large | PHP limit | upload_max_filesize, post_max_size |
| WordPress admin still shows 2MB | PHP-FPM did not read the configuration | phpinfo(), FPM version, panel configuration |
| Only API uploads fail | Reverse proxy or application limit | Nginx location, application body limit |
| Works normally after bypassing Cloudflare | CDN upload limit | Cloudflare plan limits, grey-cloud test |
| Changing the host machine in Docker has no effect | Configuration is inside the container | docker ps, container Nginx/PHP configuration |
When I encounter 413, I usually follow this order:
sudo tail -n 80 /var/log/nginx/error.log
sudo nginx -T | grep -n client_max_body_size
sudo nginx -t
After confirming Nginx, then check PHP:
php --ini
systemctl list-units | grep php | grep fpm
If it is WordPress, use phpinfo() to confirm the values actually read by the web environment.
If Cloudflare is in front, do another grey-cloud direct connection test.
For ordinary websites uploading images, themes, and plugins, this is usually enough:
Nginx:
client_max_body_size 100M;
PHP:
upload_max_filesize = 100M
post_max_size = 120M
max_execution_time = 300
max_input_time = 300
memory_limit = 256M
If you only need to upload 20MB files, do not change it directly to 1GB. Bigger is not always better; enough is enough.
The essence of 413 is "request body too large," but the real trouble is that every layer along the chain has its own upper limit. Check in the order of Nginx, PHP, application, CDN, and Docker, and you basically will not go wrong.