nginx 413 Request Entity Too Large: client_max_body_size explained

A 413 Request Entity Too Large after adding client_max_body_size 50m to nginx.conf usually means a more specific context still overrides it, or the upstream application rejects the body after nginx accepts it. The directive applies to http, server, and location blocks. Raising it at the edge only moves the failure deeper if the rest of the stack is not adjusted. This guide covers how the directive inherits, when nginx fires the 413, how proxy_request_buffering changes the failure mode, and why you must verify every hop including the upstream application.

The default client_max_body_size of 1m protects worker memory and disk from accidental or malicious payload bloat. By rejecting oversized bodies before forwarding data upstream, nginx acts as the first gate in a chain of body-size limits. Understanding that pipeline is the difference between a one-line config change and a multi-hour hunt through layers that enforce their own limits.

What it is and why it matters

client_max_body_size sets the maximum allowed size of the client request body. The default is 1m. If the body exceeds the limit, nginx terminates the request and returns HTTP 413 before the upstream application sees it. The directive accepts bytes, or the suffixes k, m, or g. Setting it to 0 disables the check.

The directive is valid in http, server, and location contexts. nginx applies the most specific matching context. A location block overrides a server default, which overrides the global http setting. The most common misconfiguration is raising the limit at the http level while a nested location still carries a smaller value, so some paths continue to reject large bodies even though the global config looks correct.

By rejecting oversized bodies at the edge, nginx prevents workers from allocating excessive memory or writing huge temporary files to disk. For reverse proxies, this is the first gate in a chain of body-size limits. Raising it here without adjusting the rest of the stack only moves the failure deeper.

How it works

nginx evaluates client_max_body_size while reading the request body, before forwarding data to an upstream. If the body exceeds the limit, nginx logs the event and returns 413 immediately. The backend never receives the oversized payload.

http {
    client_max_body_size 1m;

    server {
        listen 80;
        client_max_body_size 10m;

        location /api/v1/upload {
            client_max_body_size 100m;
        }
    }
}

A request to /api/v1/upload is allowed up to 100 MB. Any other path on that server is allowed 10 MB. A request to a different server block falls back to 1 MB. Remove the location block and /api/v1/upload inherits the server value of 10 MB. This specificity trap catches operators who raise the global limit but forget a restrictive location block.

When the body is within the limit but larger than client_body_buffer_size (8k on 32-bit x86, 16k on other 64-bit platforms), nginx writes the body to a temporary file under client_body_temp_path. This spill-to-disk behavior is invisible to the client but adds disk I/O latency and consumes space on the temp partition. No error is logged, yet latency rises for all requests handled by that worker.

The interaction with proxy_request_buffering determines when and how the upstream sees the body:

flowchart TD
    A[Client request body] --> B{Exceeds client_max_body_size?}
    B -->|Yes| C[HTTP 413]
    B -->|No| D{proxy_request_buffering}
    D -->|on| E[Buffer then proxy]
    D -->|off| F[Stream to upstream]
    E --> G[Upstream application]
    F --> G

With the default proxy_request_buffering on, nginx buffers the entire body into memory or a temporary file before opening a connection to the upstream. The 413 decision is made early, and the upstream only receives complete, validated bodies. Because the body is still available locally, nginx can retry the request on a different upstream server if the first fails during header processing.

With proxy_request_buffering off, nginx streams the body to the upstream as it arrives from the client. client_max_body_size is still enforced, but once streaming begins, nginx cannot retry on an alternative upstream because the body is already consumed. This avoids disk writes for large uploads but sacrifices resilience against upstream failure.

Where it shows up in production

The most common scenario is a reverse proxy in front of an application with file upload endpoints. An operator raises client_max_body_size in the http block, but a nested location inherits a smaller value, or a server default restricts it further. The result is uploads that work on staging but not production, or one endpoint that rejects the same file another accepts. Inspect the effective configuration with nginx -T and trace the exact context that matches the request URI.

In Kubernetes ingress environments, the controller generates nginx configuration from Ingress objects. If the generated config does not include a location-level override for the upload path, the request falls back to the controller’s default template, which may be as low as 1m. Verify the effective configuration inside the running controller pod rather than assuming an annotation took effect.

Layered infrastructure introduces another source of confusion. If nginx sits behind Cloudflare or a corporate WAF, those layers enforce their own body size limits. Cloudflare’s free tier defaults to 100 MB. ModSecurity with SecRequestBodyLimit can reject a body before it ever reaches nginx. When you see 413s in the browser but nothing in the nginx access log, the rejection is happening upstream of nginx. Check the outermost layer first.

In containerized environments, the client_body_temp_path may reside on a small overlay filesystem or tmpfs mount. Even when client_max_body_size is configured correctly, a multi-gigabyte upload with proxy_request_buffering on can exhaust the temp partition and fail with a different error, often masked as an internal server error or a connection reset. Monitor disk utilization on the temp path independently of the upload size limit.

Tradeoffs and common misuses

Setting client_max_body_size 0 disables the check entirely. This removes the safety rail and allows clients to stream unlimited data. Do not use this in production unless another layer enforces the limit.

The biggest operational trap is raising the nginx limit without touching the upstream application. PHP-FPM requires post_max_size and upload_max_filesize in php.ini to be at least as large as the nginx limit. It also needs memory_limit and max_execution_time raised to handle the processing time and memory footprint of large files. Node.js applications using Express need the body parser limit increased. If nginx allows 100 MB but the application framework rejects at 10 MB, the user sees an application-level error that looks like a successful proxy but a failed backend.

With proxy_request_buffering on (default), every large upload is written to disk first. On high-throughput upload endpoints, this creates sustained write load on the temp file partition. If you move large uploads to proxy_request_buffering off to spare the disk, ensure your upstream can handle streaming multipart bodies and that you accept the loss of upstream retry capability.

Signals to watch in production

SignalWhy it mattersWarning sign
HTTP 413 rateDirect measure of rejected uploadsSpike after a deploy or feature release
Request length ($request_length)Total bytes of request line, headers, and body; dominant on upload endpointsP95 approaching client_max_body_size
Error log entries containing “client intended to send too large body”nginx explicitly logs this rejectionSustained rate indicates a misconfigured limit
Disk utilization on temp file partitionBuffered large bodies write to disk before proxyingI/O latency rising on the partition holding client_body_temp_path
Upstream error rate on upload endpointsReveals when nginx limit is raised but upstream limit is not4xx or 5xx spikes on POST/PUT endpoints after raising client_max_body_size

How Netdata helps

  • Correlate HTTP 413 spikes with request rate and request length trends to determine whether a new feature or traffic pattern is triggering the limit.
  • Cross-reference 413 spikes with 4xx status code distributions from the access log to confirm the rejection source.
  • Track disk I/O latency and utilization on the temp file partition to catch buffer-spill pressure before it cascades into broader latency degradation.
  • Compare $request_time with $upstream_response_time to identify whether large uploads are stalling during client read or upstream processing.
  • Monitor nginx worker process memory to detect RSS growth from concurrent large uploads and extended connection duration.