Nginx Buffer Tuning: Optimizing `client_body_buffer_size` and `proxy_buffer_size`

Tune Nginx request and proxy buffers without wasting memory or causing avoidable disk buffering.

Nginx Buffer Tuning: Optimizing client_body_buffer_size and proxy_buffer_size

Nginx buffer tuning is not about making every buffer huge. It is about deciding which data should sit in memory, which data can safely spill to disk, and which responses should stream instead of being fully buffered.

The confusing part is that several directives sound similar. client_body_buffer_size applies to request bodies coming from clients. proxy_buffer_size and proxy_buffers apply to responses coming back from an upstream server when Nginx is acting as a reverse proxy. FastCGI has its own matching directives. If you tune the wrong side, nothing improves.

A good tuning session starts with symptoms:

  • Nginx error logs mention temporary files.
  • Uploads or large POST requests feel slow.
  • Reverse-proxied API responses pause under load.
  • Workers use more memory than expected.
  • Large response headers trigger upstream errors.

Those symptoms do not all have the same fix. Bigger buffers can reduce disk I/O, but they also increase per-request memory pressure. On a busy site, a setting that looks tiny per request can become large when multiplied by thousands of concurrent connections.

Request body buffering: client_body_buffer_size

client_body_buffer_size controls the buffer used while Nginx reads the body of a client request. Think form submissions, JSON POST bodies, and uploads. If the request body is larger than this buffer, Nginx may write part of it to a temporary file under client_body_temp_path.

A basic setting looks like this:

http {
    client_body_buffer_size 128k;
}

This does not change the maximum allowed upload size. That is controlled by client_max_body_size:

server {
    client_max_body_size 20m;
    client_body_buffer_size 128k;
}

Those two directives answer different questions. client_max_body_size says, "How large may the request be before Nginx rejects it?" client_body_buffer_size says, "How much of the request body should Nginx keep in memory before using a temp file?"

For a JSON API where most request bodies are under 32 KB and a few are 200 KB, a 128k buffer may reduce temp-file writes without being excessive. For a file upload service that accepts 50 MB videos, setting client_body_buffer_size 50m globally would be a bad trade. You would be reserving too much memory for a workload where disk buffering may be acceptable.

Check the error log for clues like:

[warn] a client request body is buffered to a temporary file

That warning is not automatically a failure. It is Nginx telling you a body exceeded the in-memory buffer. If it happens occasionally during large uploads, fine. If it happens constantly for ordinary API requests, tune the buffer or fix the clients sending large payloads.

Proxy response buffering: proxy_buffer_size and proxy_buffers

When Nginx proxies to an upstream app, response buffering is usually enabled by default. Nginx reads the upstream response quickly, stores it in buffers, and sends it to the client. If the response does not fit in memory buffers, part of it can be written to a temporary file. The official Nginx proxy module documentation describes this behavior and ties temp-file writing to proxy_max_temp_file_size and proxy_temp_file_write_size.

The first buffer is controlled by proxy_buffer_size:

proxy_buffer_size 16k;

This buffer handles the response header and the first part of the response. If your upstream sends large headers, such as many cookies, large auth headers, or oversized tracing metadata, you may see errors like "upstream sent too big header." In that case, proxy_buffer_size is often the directive to review.

The remaining response buffers are controlled by proxy_buffers:

proxy_buffers 8 32k;

That means eight buffers of 32 KB each for proxied response data. It does not mean every request always consumes the full amount from start to finish, but you should still treat these as per-request memory settings under load.

A common reverse proxy block looks like this:

location /api/ {
    proxy_pass http://app_backend;

    proxy_buffering on;
    proxy_buffer_size 16k;
    proxy_buffers 8 32k;
    proxy_busy_buffers_size 64k;
}

Do not copy this blindly. A small HTML site, a JSON API, and a file download service have different needs.

Temp files and proxy_max_temp_file_size

If proxied response buffering is enabled and the response is larger than the configured buffers, Nginx may write part of it to disk. proxy_max_temp_file_size limits the size of that temporary file. Nginx's default is commonly documented as 1024m, and setting it to 0 disables buffering proxied responses to temporary files.

location /api/ {
    proxy_pass http://app_backend;
    proxy_buffering on;
    proxy_max_temp_file_size 50m;
}

Use 0 carefully:

proxy_max_temp_file_size 0;

That does not mean large responses become free. It means Nginx will not use temp files for those buffered proxied responses. Depending on the response, client speed, and buffering behavior, you may need more memory, different buffering settings, or a streaming design.

One important nuance from the Nginx documentation: the proxy_max_temp_file_size restriction does not apply to responses that will be cached or stored on disk. If you use proxy_cache or proxy_store, review those settings separately.

When proxy_buffering off is the better answer

Sometimes you should not buffer the response at all. Streaming endpoints, server-sent events, large downloads, and long-running output often work better with proxy buffering disabled:

location /events/ {
    proxy_pass http://app_backend;
    proxy_buffering off;
}

With buffering off, Nginx passes the upstream response to the client as it receives it instead of trying to collect the full response. The proxy_buffer_size still matters because Nginx needs a buffer for data received from upstream, but proxy_buffers and temp-file behavior become less central.

Do not turn buffering off globally unless you understand the tradeoff. Buffering protects upstream servers from slow clients. If clients download slowly and buffering is off, upstream connections may stay occupied longer.

FastCGI buffers for PHP and similar setups

If Nginx talks to PHP-FPM, proxy_* directives are not the ones you need. FastCGI uses matching names:

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

    fastcgi_buffer_size 16k;
    fastcgi_buffers 8 32k;
}

The same logic applies: the first buffer handles headers and initial data; the buffer array handles more response content. If PHP sends large headers, fastcgi_buffer_size may need attention. If large PHP-generated pages spill to disk, review fastcgi_buffers and the workload.

How to tune without guessing

Start by checking logs:

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

Look for warnings about request bodies or upstream responses being buffered to temporary files. Then measure the actual sizes involved. For APIs, sample request and response sizes from access logs, application logs, or your observability system. For uploads, separate metadata requests from file upload endpoints. They should not share the same assumptions.

Tune in the narrowest context that makes sense. Instead of this:

http {
    client_body_buffer_size 10m;
}

prefer something like this for the upload endpoint only:

location /upload/ {
    client_max_body_size 50m;
    client_body_buffer_size 512k;
    proxy_pass http://upload_backend;
}

For a JSON API with occasional 1 MB responses, you might tune only that API location:

location /reports/ {
    proxy_pass http://report_backend;
    proxy_buffering on;
    proxy_buffer_size 32k;
    proxy_buffers 16 64k;
    proxy_max_temp_file_size 20m;
}

Then calculate the worst case. If 1,000 concurrent requests can each use several hundred KB of response buffers, that is real memory. Leave room for worker processes, TLS, upstream connections, caches, the OS page cache, and other services.

Validate the config and watch the system

After every change:

sudo nginx -t
sudo systemctl reload nginx

Then watch memory, disk I/O, and error logs. A successful syntax test only proves the file parses. It does not prove the values are good.

Useful checks include:

free -h
iostat -xz 1
sudo tail -f /var/log/nginx/error.log

If temp-file warnings disappear but memory pressure climbs, you overshot. If memory stays healthy and disk I/O drops during the affected workload, the change probably helped.

Nginx buffer tuning works best when it is local, measured, and tied to real traffic. Keep ordinary requests in memory when that saves latency, let genuinely large uploads or downloads use the right path, and avoid global settings that make every connection pay for the largest edge case.