nginx: a client request body is buffered to a temporary file — what it means

You are tailing the nginx error log during a latency investigation and see the line: a client request body is buffered to a temporary file. It is logged at [warn], not [error], so it is easy to ignore. But the message means a request body has exceeded the in-memory buffer and nginx is now writing that data to disk. On a busy reverse proxy or file-upload endpoint, this behavior can add hundreds of milliseconds or seconds of latency before your upstream application receives the payload. It also consumes file descriptors and disk I/O capacity without ever surfacing as a 5xx status code.

By the end of this article, you will understand the exact mechanics of request body buffering, how client_body_buffer_size controls the threshold, and when to tune the buffer versus disable buffering entirely.

What it is and why it matters

nginx uses a fixed-size per-request memory buffer for client request bodies. The default size is 8KB on x86-64 platforms. When a request body exceeds this limit, nginx streams the remainder to a temporary file on disk and emits the warning. The directive client_body_temp_path controls where those files are written.

This is operationally significant because nginx workers run a single-threaded event loop. Disk I/O is slower than RAM by orders of magnitude, and while nginx handles I/O efficiently, writing large bodies to temporary files introduces stall. If the temporary directory shares a disk with access logs or system swap, the stall compounds. The upstream application does not receive the body until the write completes, so operators frequently misdiagnose the resulting latency as upstream slowness.

The playbook classifies buffer spill to disk as a characteristic failure archetype: disk I/O spikes, latency explodes, and critically, it is silent. Unlike a 502 or 504, the request eventually completes with a 200 or similar success code. The only immediate evidence is the warning log line and the elevated tail latency.

How it works

After nginx finishes reading request headers, it enters the body-reading phase. It allocates a buffer sized by client_body_buffer_size and begins receiving the body from the client.

If the body fits entirely within the buffer, nginx keeps it in memory and passes it to the request handler or upstream once the full body arrives.

If the body exceeds the buffer, nginx opens a temporary file and writes the overflow to disk. The worker continues reading from the client and appending to the file until the entire body is received. Only then does nginx proceed to the next phase: proxying the request, invoking a FastCGI handler, or serving the location.

Throughout this transfer, the temporary file holds a file descriptor from the worker’s pool. The playbook notes that temp files for large bodies consume FDs during transfer. In environments where the per-worker file descriptor limit is constrained, common in containers with default ulimits, a burst of concurrent uploads can exhaust FDs and block the worker from accepting new connections or opening upstream connections.

The disk write itself is the hidden bottleneck. The playbook flags temp file I/O as a source of latency spikes. Because the body must be fully written before upstream forwarding, the time spent is pure nginx-side overhead. It does not appear in $upstream_response_time. The delay shows up as a growing gap between $request_time and $upstream_response_time, which the playbook identifies as the most commonly misinterpreted relationship in nginx monitoring.

flowchart TD
    A[Client sends request body] --> B{Fits in client_body_buffer_size?}
    B -->|Yes| C[Hold in worker memory]
    B -->|No| D[Write to temporary file]
    C --> E[Forward to upstream/handler]
    D --> E
    E --> F[Upstream processes request]

Where it shows up in production

The warning appears wherever clients send bodies larger than the configured memory threshold. Typical production triggers include:

  • Multipart file uploads. Image, video, or document uploads through a web application almost always exceed 8KB.
  • Bulk API ingest. Large JSON, CSV, or protobuf payloads posted to analytics or data-import endpoints routinely spill to disk.
  • Containerized ingress controllers. Containers frequently run with lower default file descriptor limits, amplifying the impact of temp file FD consumption.
  • Shared storage backends. If client_body_temp_path points to network-attached storage, a seemingly local buffering operation becomes network I/O with unpredictable latency.

One common antipattern is raising client_max_body_size to accommodate larger uploads without also raising client_body_buffer_size. The upload is now permitted, but every large upload is guaranteed to hit disk. If your storage driver is not overlay2, the temp path and performance characteristics may differ from standard expectations.

Tradeoffs and when to use it

The default behavior, buffer in memory and spill to disk when full, is conservative. It allows nginx to validate the complete body and present a fully materialized payload to the upstream. For many workloads, this is correct.

Tuning client_body_buffer_size: If your typical upload or API payload is 256KB and you have memory headroom, raising the buffer to match keeps those requests entirely in RAM. The tradeoff is memory per connection. In proxy deployments, remember the connection multiplier: each proxied request uses at least two connection slots, and larger buffers amplify total memory pressure. Do not size the buffer for outliers like 100MB uploads.

Disabling request buffering with proxy_request_buffering off: For streaming uploads where the upstream must receive data immediately, or where disk writes are unacceptable, disabling buffering passes the client stream through to the upstream as data arrives. The source notes highlight this pattern for streaming uploads. The tradeoff is that the upstream receives data immediately and must be prepared to handle streaming input; error recovery becomes the upstream’s responsibility.

Temp path isolation: If disk spilling is unavoidable, place the temp directory on fast local storage that is isolated from access logs and system partitions. The playbook warns that synchronous log writes during incidents compound disk I/O pressure. Collocating temp files with logs multiplies that risk.

Signals to watch in production

SignalWhy it mattersWarning sign
Error log warning rateConfirms upload workload is exceeding memory buffersSustained buffered to a temporary file messages on upload endpoints
Disk I/O latency on temp/log partitionTemp file writes stall the worker event loop$request_time spikes with flat $upstream_response_time
File descriptor usage per workerEach buffered body holds an FD during the transferFD count climbs without a matching rise in active connections
Gap between request time and upstream response timeReveals pure nginx-side buffering delay$request_time consistently exceeds $upstream_response_time for large uploads
Active connections in Writing stateWorkers spend time waiting on disk I/O during body bufferingWriting count elevated during upload traffic with low upstream latency

How Netdata helps

  • Correlate nginx error log warning spikes with per-disk I/O latency charts on the same node to confirm temp-file stall.
  • Track per-process file descriptor utilization to catch FD accumulation before it blocks connection acceptance.
  • Overlay $request_time and $upstream_response_time from access logs to surface the buffering delay gap in real time.
  • Monitor disk latency on the partition holding client_body_temp_path to detect silent saturation before it affects request latency.