Site icon GetPageSpeed

NGINX client_max_body_size: Fix 413 Errors

NGINX client_max_body_size: Fix 413 Request Entity Too Large Error

Are you seeing the dreaded 413 Request Entity Too Large error? The NGINX client_max_body_size directive is almost certainly the cause. This directive controls the maximum size of the client request body. When exceeded, NGINX rejects the request immediately.

In this guide, you will learn how to configure NGINX client_max_body_size properly. You will fix upload errors and handle large file transfers.

Understanding NGINX client_max_body_size

The client_max_body_size directive sets the maximum size of the client request body. NGINX checks the Content-Length header. If the size exceeds the limit, it returns a 413 error.

By default, NGINX sets client_max_body_size to 1 megabyte. This default is often too small for modern applications. File uploads, API payloads, and form submissions frequently need larger limits.

Where You Can Use client_max_body_size

You can set this directive in three contexts:

The specific context overrides the general one. Location blocks override server blocks. Server blocks override http. This model allows flexible configuration.

Quick Fix: Setting client_max_body_size

If you see the 413 error and need a quick fix, add this to your server block:

server {
    listen 80;
    server_name example.com;

    client_max_body_size 64m;

    # ... rest of your configuration
}

This sets the maximum body size to 64 megabytes. Test and reload your configuration:

nginx -t && nginx -s reload

Under the Hood: How NGINX Checks Body Size

Understanding the size check helps you troubleshoot issues. The behavior differs by transfer method.

Content-Length Based Requests

For standard requests with a Content-Length header, NGINX checks immediately. The check happens after parsing headers but before reading body data. NGINX rejects oversized requests without wasting bandwidth.

When the check fails, NGINX logs:

client intended to send too large body: 10485760 bytes

The number shows how many bytes the client attempted to send.

Chunked Transfer Encoding

For chunked requests, NGINX tracks size incrementally. It sums each chunk as it arrives. If the total exceeds the limit, you see:

client intended to send too large chunked body: 5242880+1048576 bytes

This format shows received bytes plus the current chunk size.

HTTP/2 and HTTP/3 Behavior

Modern protocols handle size checking similarly. NGINX tracks a received counter as data frames arrive. The same limit applies. No special configuration is needed.

Size Format Options

NGINX accepts several size formats:

Format Example Description
Bytes 1048576 Size in bytes (1MB)
Kilobytes 1024k Size in kilobytes
Megabytes 64m Size in megabytes
Gigabytes 2g Size in gigabytes

For readability, use the appropriate unit:

client_max_body_size 67108864;   # bytes (less readable)
client_max_body_size 65536k;     # kilobytes
client_max_body_size 64m;        # megabytes (recommended)

Common Mistake: Missing Unit Suffix

A frequent error is forgetting the size suffix:

# WRONG - This is 64 bytes, not 64 megabytes!
client_max_body_size 64;

# CORRECT - 64 megabytes
client_max_body_size 64m;

Without a suffix, NGINX interprets the value as bytes. This causes 413 errors.

Setting client_max_body_size by Context

HTTP Context (Global Setting)

To set a global default, add the directive to the http block:

http {
    client_max_body_size 32m;

    # This applies to all server blocks
    include /etc/nginx/conf.d/*.conf;
}

This works well for consistent limits. Server blocks can still override this value.

Server Context (Per Virtual Host)

For per-site configuration, add the directive to a server block:

server {
    listen 80;
    server_name uploads.example.com;
    root /var/www/uploads;

    # This site handles large file uploads
    client_max_body_size 500m;

    location / {
        try_files $uri $uri/ =404;
    }
}

Location Context (Per URL Pattern)

Different endpoints may need different limits. Use location blocks:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;

    # Default limit for the site
    client_max_body_size 10m;

    # Small limit for avatar uploads
    location /api/avatar {
        client_max_body_size 1m;
    }

    # Large limit for video uploads
    location /api/videos {
        client_max_body_size 500m;
    }

    # Admin area needs larger imports
    location /admin/import {
        client_max_body_size 2g;
    }
}

The Inheritance Gotcha with try_files

There is a critical edge case with client_max_body_size inheritance. When requests are internally rewritten via try_files, the directive behaves unexpectedly.

The problem: After an internal rewrite, client_max_body_size cannot be increased in the rewritten location. Only more restrictive values apply. More permissive values are ignored.

Consider this configuration:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;

    # Default 1MB limit
    client_max_body_size 1m;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        # This 64m limit will NOT apply after try_files rewrite!
        client_max_body_size 64m;

        fastcgi_pass unix:/run/php-fpm/www.sock;
        include fastcgi_params;
    }
}

When /upload rewrites to /index.php, the 64m limit is ignored. The 1m server limit applies instead.

Solutions:

  1. Set the limit at server level (less secure, exposes all paths):
    server {
        client_max_body_size 64m;
        # ...
    }
    
  2. Create an explicit location for upload endpoints (recommended):
    location = /upload {
        client_max_body_size 64m;
    
        fastcgi_pass unix:/run/php-fpm/www.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
    }
    

For a deep dive into this behavior, see our article on client_max_body_size inheritance.

Disabling the Size Limit Entirely

Setting client_max_body_size to 0 disables size checking:

location /internal/sync {
    client_max_body_size 0;
}

When zero, NGINX skips the size comparison. The request body can be any size. NGINX source code confirms this: it checks if the value is non-zero before comparing.

Warning: Use with extreme caution. Without limits, malicious clients could send huge requests. This could exhaust server resources. Only use zero for trusted internal endpoints.

Complete Configuration Examples

WordPress Configuration

WordPress sites need increased limits for media uploads:

server {
    listen 80;
    server_name wordpress.example.com;
    root /var/www/wordpress;
    index index.php index.html;

    # WordPress general upload limit - set at server level
    # to avoid try_files inheritance issues
    client_max_body_size 64m;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # WooCommerce and large imports in admin
    location ~ ^/wp-admin/.*\.php$ {
        client_max_body_size 256m;

        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # Timeouts for large uploads
        fastcgi_read_timeout 300s;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

API Server Configuration

Modern APIs need to handle large payloads:

server {
    listen 80;
    server_name api.example.com;

    # Default API limit
    client_max_body_size 16m;

    # Optimize for API requests
    client_body_buffer_size 128k;
    client_body_timeout 60s;

    location /v1/upload {
        client_max_body_size 100m;

        proxy_pass http://backend;
        proxy_request_buffering off;
    }

    location /v1/ {
        proxy_pass http://backend;
    }
}

Reverse Proxy Configuration

For reverse proxy setups, adjust limits accordingly:

upstream backend {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name app.example.com;

    client_max_body_size 50m;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # For large uploads, disable request buffering
        proxy_request_buffering off;
    }
}

Several directives work with client_max_body_size. These control body handling.

client_body_buffer_size

Sets the buffer size for reading request bodies:

client_body_buffer_size 128k;

Default is typically 8KB (two memory pages). When exceeded, NGINX writes to a temp file. Larger buffers improve upload performance.

client_body_timeout

Sets a timeout for reading the request body:

client_body_timeout 60s;

The timeout applies between read operations, not the entire transfer. Increase for large uploads over slow connections. For timeout issues, see our guide on fixing 504 Gateway Timeout errors.

client_body_temp_path

Specifies the temporary file directory:

http {
    client_body_temp_path /var/lib/nginx/tmp/client_body 1 2;
}

NGINX workers must have write permissions to this directory.

PHP Configuration: The Other Half

With PHP-FPM, client_max_body_size is only half the solution. PHP has its own limits. For PHP-FPM performance, review our guide on NGINX FastCGI Keepalive.

PHP Settings That Affect Uploads

Check your current PHP limits:

php -i | grep -E "(upload_max_filesize|post_max_size|memory_limit)"

Default values are restrictive:

upload_max_filesize = 2M
post_max_size = 8M
memory_limit = 128M

Aligning NGINX and PHP Limits

Your limits must be aligned:

memory_limit > post_max_size >= upload_max_filesize
NGINX client_max_body_size >= post_max_size

For 64MB uploads, configure both:

NGINX:

client_max_body_size 64m;

PHP (php.ini or pool config):

upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M

For pool-specific settings:

; /etc/php-fpm.d/www.conf
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
php_admin_value[memory_limit] = 256M

Troubleshooting 413 Errors

Check Your Current Configuration

Find where client_max_body_size is set:

grep -r "client_max_body_size" /etc/nginx/

Verify the Applied Limit

Location blocks override server blocks. Server blocks override http. But remember: internal rewrites can prevent location overrides from applying.

Check NGINX Error Log

The error log shows rejected requests:

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

A 413 error shows:

client intended to send too large body: 10485760 bytes

Testing Your Configuration

Create a test file and verify:

# Create a 50MB test file
dd if=/dev/zero of=/tmp/testfile bs=1M count=50

# Test upload
curl -X POST -F "file=@/tmp/testfile" http://localhost/upload

Performance Considerations

Memory Usage

The client_body_buffer_size affects memory directly:

Memory = concurrent_uploads Γ— client_body_buffer_size

With 100 uploads and 1MB buffers, you need 100MB of memory.

Disk I/O

When bodies exceed the buffer, NGINX writes temp files. Use fast storage for client_body_temp_path.

Request Buffering

For proxies, proxy_request_buffering affects handling:

# Buffer entire request before forwarding (default)
proxy_request_buffering on;

# Stream to backend as data arrives
proxy_request_buffering off;

Disabling buffering reduces memory. Your backend must handle slow clients.

Security Considerations

Protect Against Resource Exhaustion

Large limits enable denial-of-service attacks. Apply generous limits only where needed:

# Restrictive default
client_max_body_size 1m;

# Generous only for specific paths
location /upload {
    client_max_body_size 100m;

    # Add rate limiting
    limit_req zone=uploads burst=5 nodelay;
}

For rate limiting, see our guide on NGINX rate limiting.

Use Authentication

Combine large limits with authentication:

location /admin/import {
    client_max_body_size 2g;

    auth_basic "Admin Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

Monitor Upload Activity

Log large uploads:

log_format uploads '$remote_addr - $request_length bytes - $request_uri';

location /upload {
    client_max_body_size 100m;
    access_log /var/log/nginx/uploads.log uploads;
}

Common Mistakes to Avoid

Mistake 1: Wrong Context

Placing the directive outside valid contexts fails:

# Wrong - not in http, server, or location
client_max_body_size 64m;

http {
    # ...
}

Mistake 2: Forgetting PHP Limits

Only increasing NGINX limits causes different errors when PHP limits remain low.

Mistake 3: Not Reloading

After changes, reload NGINX:

nginx -t && nginx -s reload

Mistake 4: Typos in Size Format

# Wrong - missing suffix (64 bytes!)
client_max_body_size 64;

# Correct
client_max_body_size 64m;

Mistake 5: Ignoring try_files Behavior

Setting limits in PHP locations won’t work if requests arrive via try_files rewrites. Set the limit at server level or use explicit location matches.

Battle-tested values for common scenarios:

Use Case Value Notes
Basic website 10m Sufficient for contact forms
WordPress blog 64m Handles most media uploads
E-commerce 128m Product images and imports
Video platform 500m – 2g Depends on video quality
API endpoint 16m JSON payloads rarely exceed this
Internal sync 0 Only for trusted networks

Summary

The NGINX client_max_body_size directive controls upload sizes. Key points:

For WordPress sites, 64m at the server level provides a good balance. For large files, adjust based on your needs.

Always test with nginx -t before reloading.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience β€’ Maintainer of GetPageSpeed RPM repository β€’ Contributor to open-source NGINX modules

Exit mobile version