Fix Nginx 413 Request Entity Too Large Error

beginnerโšก Nginx2026-03-18| Nginx 1.x on Ubuntu/Debian/CentOS, commonly triggered behind reverse proxy setups with PHP-FPM, Node.js, or file upload endpoints

Error Message

413 Request Entity Too Large
#nginx#upload#client_max_body_size

The Situation

It's 2 AM. A user reports they can't upload anything โ€” just a blank page or a vague browser error. You check /var/log/nginx/error.log:

2025/03/16 02:13:44 [error] 1234#1234: *42 client intended to send too large body: 12582912 bytes, client: 203.0.113.55, server: example.com, request: "POST /upload HTTP/1.1", host: "example.com"

12,582,912 bytes โ€” that's 12MB. The browser shows a clean:

413 Request Entity Too Large

Nginx killed the request before it touched your app. There are two layers to the fix โ€” miss the second one and the problem comes straight back.

Why This Happens

Nginx enforces a hard cap on incoming request body size. The default is 1MB (client_max_body_size 1m). Any POST request โ€” file uploads, JSON payloads, form submissions โ€” that exceeds this limit gets dropped with a 413 immediately.

This is an Nginx-level gate, completely separate from your application's own upload limits. PHP's upload_max_filesize, Node.js body parser settings, your S3 presigned URL config โ€” none of it matters if Nginx blocks the request first. Even if your app accepts 100MB files, Nginx kills anything over 1MB unless you explicitly tell it otherwise.

Quick Fix (Live Server)

First, find out what's actually active:

# Check which config is active
nginx -T | grep -n client_max_body_size

# Or find all nginx config files
grep -r client_max_body_size /etc/nginx/

No output means the default (1MB) is in effect โ€” the directive was never explicitly set. Add or update it in your server block:

sudo nano /etc/nginx/sites-available/your-site.conf

Add inside server {} or the specific location {} block:

server {
    listen 80;
    server_name example.com;

    # Allow up to 20MB uploads
    client_max_body_size 20m;

    location / {
        proxy_pass http://localhost:3000;
    }
}

Test and reload โ€” no downtime needed:

sudo nginx -t && sudo systemctl reload nginx

Permanent Fix โ€” Set It at the Right Scope

Where you put client_max_body_size matters. Nginx applies the innermost matching block, so you can get as granular as a single endpoint:

Option 1: Global (all virtual hosts)

In /etc/nginx/nginx.conf, inside the http {} block:

http {
    client_max_body_size 20m;
    ...
}

Option 2: Per virtual host

Inside the specific server {} block โ€” overrides whatever the global value is:

server {
    server_name api.example.com;
    client_max_body_size 50m;
    ...
}

Option 3: Per location (most precise)

When only one endpoint needs a higher limit, pin it there. Everything else stays locked at the default:

server {
    server_name example.com;
    client_max_body_size 1m;  # default for everything

    location /api/upload {
        client_max_body_size 100m;  # only for this endpoint
        proxy_pass http://localhost:3000;
    }
}

Disable the limit entirely (not recommended)

Setting it to 0 removes the check completely. Only use this on internal or trusted networks:

client_max_body_size 0;

If You're Using PHP-FPM

Nginx will pass the request through now โ€” but PHP might still reject it. Check these two settings:

# Find your php.ini
php --ini | grep "Loaded Configuration"

# Edit it
sudo nano /etc/php/8.1/fpm/php.ini

Both values need to match or exceed your Nginx limit:

upload_max_filesize = 20M
post_max_size = 25M

post_max_size must be larger than upload_max_filesize. It covers the entire POST body โ€” a 20MB file inside a form submission takes slightly more than 20MB total once field data is included.

Restart PHP-FPM to apply:

sudo systemctl restart php8.1-fpm

If You're Behind Another Proxy (AWS ALB, Cloudflare, etc.)

When Nginx sits behind a load balancer or CDN, that outer layer may enforce its own size limit:

  • AWS ALB: No practical cap for EC2/container targets โ€” usually not the culprit here
  • Cloudflare Free/Pro: 100MB upload limit by default
  • AWS API Gateway: Hard 10MB cap โ€” this one cannot be raised

Spot the difference: if the 413 arrives with a Cloudflare-branded error page instead of Nginx's plain text response, the block is happening on their side. No change to nginx.conf will fix that.

Verify the Fix

Use curl to test with an actual file of known size:

# Generate a 15MB test file
dd if=/dev/urandom of=/tmp/testfile bs=1M count=15

# Try uploading it
curl -v -X POST https://example.com/api/upload \
  -F "file=@/tmp/testfile" 2>&1 | grep -E "< HTTP|413|200"

After a successful fix:

< HTTP/1.1 200 OK

Still seeing 413? Confirm the reload actually took effect:

# Confirm the active config was reloaded
nginx -T | grep client_max_body_size

# Check Nginx is running the config you think it is
sudo nginx -t

Tail the error log while resending the request โ€” it'll tell you exactly what's still blocking:

sudo tail -f /var/log/nginx/error.log

Sizing Recommendations

  • API endpoints (JSON only): Keep at 1โ€“4MB. A 50MB JSON payload is a design problem, not a size limit problem.
  • Image uploads: 10โ€“20MB covers most cases, including RAW photos from modern cameras.
  • Video/document uploads: 50โ€“200MB depending on your use case. For anything over 50MB, consider chunked uploads โ€” they're more reliable over flaky connections anyway.
  • Internal admin tools: Can go higher since you control exactly who has access.

Don't set client_max_body_size higher than necessary on public endpoints. A flood of oversized requests ties up backend workers fast โ€” it's an easy vector for DoS. Scope the limit as tightly as the feature allows.

Related Error Notes