Datanest Digital — nginx-config-templates v1.0.0
This guide covers the key Nginx performance tuning parameters with real-world recommendations based on server specs and traffic patterns.
# Rule: 1 worker per CPU core
worker_processes auto; # auto-detects CPU count (recommended)
# worker_processes 4; # manual: for a 4-core serverWhy it matters: Each worker is single-threaded. More workers than cores = context switching overhead. Fewer = wasted CPU capacity.
Check your cores: nproc or lscpu | grep "^CPU(s)"
events {
worker_connections 4096;
multi_accept on;
use epoll;
}| Server Type | Recommended worker_connections |
|---|---|
| Small VPS (1-2 GB) | 1024 |
| Medium (4-8 GB) | 4096 |
| Large (16+ GB) | 8192-16384 |
Total max connections = worker_processes x worker_connections
File descriptor limit: Set worker_rlimit_nofile to at least 2x worker_connections:
worker_rlimit_nofile 65535;Also set the system limit: ulimit -n 65535 or edit /etc/security/limits.conf.
Buffers determine how Nginx stores request/response data in memory before processing.
# Request body buffer (e.g., form submissions, JSON payloads)
client_body_buffer_size 128k; # Increase to 256k for large forms
# Request header buffer
client_header_buffer_size 1k; # Sufficient for most headers
large_client_header_buffers 4 16k; # For large cookies or auth tokens
# Maximum upload size
client_max_body_size 50m; # Adjust for file uploads# Buffer for the first part of the response (headers)
proxy_buffer_size 4k;
# Buffers for the response body
proxy_buffers 8 16k; # 8 buffers of 16k each = 128k total
proxy_busy_buffers_size 32k; # Max size to send while still bufferingRule of thumb: If upstream_response_time is high but request_time is low, your buffers are probably fine. If both are high, increase buffers.
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;keepalive_timeout 65; # How long to keep idle client connections open
keepalive_requests 1000; # Max requests per keepalive connection| Setting | Low Traffic | High Traffic |
|---|---|---|
| keepalive_timeout | 65s | 30s |
| keepalive_requests | 100 | 1000-10000 |
Critical for performance — avoids TCP handshake + TLS negotiation for every request to your backend.
upstream backend {
server 127.0.0.1:3000;
keepalive 32; # Pool of 32 persistent connections
keepalive_requests 1000; # Max requests per connection
keepalive_timeout 60s; # Idle timeout
}
location / {
proxy_http_version 1.1; # Required for keepalive
proxy_set_header Connection ""; # Clear "close" header
proxy_pass http://backend;
}Sizing: Set keepalive to roughly your average concurrent backend connections. Monitor with ss -tn | grep :3000 | wc -l.
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5; # 1-9, sweet spot is 4-6
gzip_min_length 256; # Don't compress tiny responses| Level | Compression Ratio | CPU Usage | Best For |
|---|---|---|---|
| 1-2 | Low | Minimal | High-traffic, CPU-constrained |
| 4-6 | Good | Moderate | Most servers (recommended) |
| 7-9 | Marginal gain | High | Pre-compressed static files only |
Tip: For static files, pre-compress with gzip -9 or brotli at build time and use gzip_static on; or brotli_static on; to serve the pre-compressed version with zero runtime CPU cost.
# Immutable assets (hashed filenames from build tools)
location ~* \.[a-f0-9]{8,}\.(css|js|woff2)$ {
expires max; # Cache forever
add_header Cache-Control "public, immutable";
}
# Mutable assets (images, fonts)
location ~* \.(jpg|png|gif|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
# HTML (short cache for quick deploys)
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}proxy_cache_path /var/cache/nginx/app
levels=1:2
keys_zone=app_cache:64m # 64MB metadata = ~500K cached items
max_size=2g # 2GB on disk
inactive=60m; # Evict after 60 min without access
location /api/ {
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500;
proxy_cache_background_update on;
proxy_cache_lock on;
}Cache hit rate target: >80% for static content, >50% for API responses.
Monitor: Check X-Cache-Status header (HIT/MISS/BYPASS/STALE).
# Client timeouts
client_body_timeout 30s; # Time to receive request body
client_header_timeout 30s; # Time to receive request headers
send_timeout 30s; # Time between successive writes to client
# Proxy timeouts
proxy_connect_timeout 5s; # Time to establish upstream connection
proxy_send_timeout 60s; # Time to send request to upstream
proxy_read_timeout 60s; # Time to receive response from upstreamFor long-running operations (file uploads, reports):
location /api/reports/ {
proxy_read_timeout 300s; # 5 minutes
}For WebSocket:
location /ws/ {
proxy_read_timeout 3600s; # 1 hour
proxy_send_timeout 3600s;
}| Metric | How to Check | Target |
|---|---|---|
| Active connections | nginx -s status or stub_status | < worker_processes x worker_connections |
| Request time | access log $request_time | < 500ms (p99) |
| Upstream response time | access log $upstream_response_time | < 200ms (p99) |
| Cache hit rate | X-Cache-Status header | > 80% |
| Error rate | error log volume | < 0.1% |
location /nginx_status {
stub_status on;
allow 127.0.0.1;
deny all;
}# Current connections
ss -tn | awk '{print $4}' | sort | uniq -c | sort -rn | head
# Requests per second (from access log)
tail -f /var/log/nginx/access.log | pv -l -i 5 > /dev/null
# Top requested URLs
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# Slow requests (> 1 second)
awk -F'"' '$0 ~ /request_time/ { if ($NF > 1.0) print }' /var/log/nginx/access.logDatanest Digital | datanest.dev | MIT License
Production-ready Nginx configurations for every use case.
Stop copy-pasting from StackOverflow. Ship secure, optimized Nginx configs in minutes.



Get the full Nginx Config Templates and unlock everything.
Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.
Access all interactive tools with complete data, all workload profiles, and the full scenario library.
Downloadable source code, configuration files, and working examples from every chapter.
Free updates for life. Every new chapter, tool, and improvement included.