NGINX client_max_body_size Not Working in Location Block: Debugging File Upload Limits


2 views

While NGINX documentation states client_max_body_size can be set within location blocks, many administrators encounter situations where these overrides fail to take effect. The 413 "Request Entity Too Large" error persists despite proper configuration.

Here's a typical problematic configuration:

server {
    client_max_body_size 10M;
    
    location /uploads {
        client_max_body_size 100M;
        # This override frequently fails
    }
}

The root cause often lies in request processing order. When PHP processing occurs (common for admin panels), the request body size check happens before reaching the location block.

Solution 1: Move the directive to server context with conditions

server {
    set $upload_limit 10M;
    
    if ($uri ~ ^/admin) {
        set $upload_limit 256M;
    }
    
    client_max_body_size $upload_limit;
}

Solution 2: Ensure proper location matching

location ^~ /admin {
    client_max_body_size 256M;
    
    # Explicitly handle PHP processing
    location ~ \.php$ {
        fastcgi_param PHP_VALUE "upload_max_filesize=256M \n post_max_size=256M";
        include fastcgi_params;
    }
}

For an admin panel with PHP file uploads:

server {
    listen 80;
    server_name example.com;
    
    # Default limit
    client_max_body_size 16M;
    
    location /admin {
        # Override for admin area
        client_max_body_size 256M;
        
        # PHP handler with matching settings
        location ~ \.php$ {
            fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
            fastcgi_param PHP_VALUE "upload_max_filesize=256M \n post_max_size=256M";
            include fastcgi_params;
        }
    }
}

Check effective values using:

curl -I -X POST -H "Content-Length: 257000000" http://example.com/admin/test

Inspect NGINX debug logs for body size checks:

error_log /var/log/nginx/debug.log debug;

The client_max_body_size directive in NGINX can indeed be placed in http, server, or location blocks, with inner blocks overriding outer ones. However, there's a common pitfall when dealing with PHP processing.

When you're dealing with PHP file uploads, the request might be processed by a different location block (typically location ~ \.php$) before reaching your /admin location. Here's what typically happens:

server {
    # This applies to all requests
    client_max_body_size 16M;

    location /admin {
        # This gets ignored for PHP files
        client_max_body_size 256M;
    }

    location ~ \.php$ {
        # PHP files end up here with the 16M limit
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    }
}

To properly handle this, you need to:

  1. Set the global default (16M in http block)
  2. Override for your admin area
  3. Ensure the PHP handler respects this override

Here's the working configuration:

http {
    client_max_body_size 16M;

    server {
        server_name example.com;
        root /var/www/example.com;

        location /admin {
            client_max_body_size 256M;
            
            # Important: Pass to PHP handler with inherited size
            location ~ \.php$ {
                include fastcgi_params;
                fastcgi_pass unix:/run/php/php8.1-fpm.sock;
            }
        }

        location ~ \.php$ {
            include fastcgi_params;
            fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        }
    }
}

If nested locations aren't feasible in your setup, consider these options:

# Option 1: Match admin PHP files separately
location ~ ^/admin/.+\.php$ {
    client_max_body_size 256M;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}

# Option 2: Use a regex location
location ~ /admin {
    client_max_body_size 256M;
    try_files $uri $uri/ /index.php?$args;
}

After making changes, always:

sudo nginx -t
sudo systemctl reload nginx

Test with a large file upload using curl:

curl -X POST -H "Content-Type: multipart/form-data" \
  -F "file=@large_file.zip" http://example.com/admin/upload.php