Optimizing Nginx Proxy Buffering for Large File Responses: Practical Solutions to Avoid Log Flooding


2 views

When working with Nginx as a reverse proxy for Python applications (particularly with Gunicorn), we often encounter a specific challenge with large responses. The default configuration triggers warnings like:

an upstream response is buffered to a temporary file
/path/to/nginx/proxy_temp/4/86/0000000864 while reading upstream,
client: 1.2.3.4, server: api.ourdomain.com, request: "GET /pdf/..."

Nginx uses a sophisticated buffering system with these key components:

  • proxy_buffer_size: Default 4K/8K (usually one memory page)
  • proxy_buffers: Number and size of buffers (default 8 buffers of proxy_buffer_size)
  • proxy_max_temp_file_size: Controls temp file usage (default 1GB)
  • proxy_busy_buffers_size: Controls when buffers are flushed to client

Instead of the problematic approaches mentioned in the original question, consider these production-tested solutions:

1. Selective Buffer Configuration

Configure different buffer settings for specific locations:

location /small_responses {
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
    proxy_busy_buffers_size 8k;
}

location /large_files {
    proxy_buffer_size 8k;
    proxy_buffers 16 8k;
    proxy_busy_buffers_size 16k;
    proxy_max_temp_file_size 1024m;
}

2. Adjusting Log Levels

The most elegant solution is to change the log level for these specific messages:

http {
    # Set main error log level
    error_log /var/log/nginx/error.log warn;
    
    # Change specific message to notice level
    map $upstream_http_content_length $loggable {
        default 1;
        "~^[0-9]{5,}" 0;  # Skip logging for responses > 9999 bytes
    }
    
    server {
        error_log /var/log/nginx/server_error.log warn;
        if ($loggable = 0) {
            access_log off;
        }
    }
}

3. Optimizing Temporary File Handling

For better performance with large files:

proxy_temp_path /var/cache/nginx/temp levels=1:2 keys_zone=temp_zone:10m inactive=1d max_size=10g;

When implementing these solutions, keep in mind:

  • Memory usage vs. disk I/O tradeoffs
  • Impact on slow clients (especially for large PDFs)
  • Worker process efficiency
  • Overall system resource utilization
  1. Monitor buffer usage with Nginx metrics
  2. Implement proper caching for large, static-like responses
  3. Consider using X-Accel-Redirect for very large files
  4. Regularly rotate and analyze proxy_temp files

When working with Nginx as a reverse proxy (particularly for Python applications using Gunicorn), we often encounter this log spam:

an upstream response is buffered to a temporary file
/path/to/nginx/proxy_temp/4/86/0000000864 while reading upstream,
client: 1.2.3.4, server: api.example.com, request: "GET /pdf/..."

This occurs when responses exceed the default buffer size (typically 4K or 8K). While technically a "warning", it's actually Nginx working as designed - just being overly verbose about it.

Contrary to what the warning suggests, temporary file buffering is a feature - not a failure. It prevents slow clients from tying up backend workers. For PDF generation endpoints that produce 1-2MB responses, this is exactly what we want.

Instead of disabling buffering (which would be dangerous), we should tune the parameters:

# In nginx.conf or your server block
proxy_buffering on;
proxy_buffer_size 8k;  # Header buffer
proxy_buffers 8 8k;    # Number and size of buffers
proxy_busy_buffers_size 16k;
proxy_temp_path /var/nginx/proxy_temp;

The most elegant solution is to modify the log level for these specific messages:

error_log /var/log/nginx/error.log warn;
error_log /var/log/nginx/buffering.log notice;

Then in your location block handling large responses:

location /pdf/ {
    proxy_pass http://gunicorn_backend;
    proxy_temp_file_write_size 1m;
    log_subrequest on;
    access_log off;
    error_log /var/log/nginx/buffering.log notice;
}

The key metrics to monitor are:

  • proxy_temp directory usage (df -h /var/nginx)
  • I/O wait during peak PDF generation periods (iostat -x 1)
  • Gunicorn worker utilization during slow client scenarios

For mixed-size endpoints, consider this pattern:

map $sent_http_content_length $should_buffer {
    default "on";
    ~^[0-9]{1,5}$ "off";  # Files under 100KB
}

server {
    proxy_buffering $should_buffer;
    # ...
}