Nginx is known for its high performance, but its default configuration is designed for average workloads. High-traffic projects need fine-tuning: worker processes, compression, caching, buffers, keepalive, and request limiting. This article covers practical settings and configuration examples. Generate a basic config with Nginx Generator on reChecker. Check HTTP/2 and Brotli with the HTTP/2 Checker, page load metrics are available in Web Vitals.
Worker processes and connections
The basic process and connection settings are specified in nginx.conf:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}worker_processes
auto— based on the number of CPU cores (recommended)- An explicit number — if you need to reserve cores for other tasks
worker_connections
The maximum number of simultaneous connections per worker. Total throughput: worker_processes × worker_connections. For 4 cores and 4096 connections, that means up to 16384 simultaneous connections.
worker_rlimit_nofile
The limit on open file descriptors. It must be at least worker_connections. On Linux, check ulimit -n and increase it in systemd or /etc/security/limits.conf if necessary.
multi_accept
When set to on, a worker accepts all new connections in the queue in a single call. This reduces latency during traffic spikes.
use epoll
The multiplexing mechanism. epoll is for Linux; kqueue is for FreeBSD/macOS. On modern systems, it is usually detected automatically.
Gzip and Brotli compression
Compression reduces the amount of data transferred and speeds up page loading.
Gzip (built into Nginx)
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/xml+rss
application/rss+xml
image/svg+xml;Settings:
gzip_comp_level 5— a balance between speed and compression ratio (1–9)gzip_min_length 256— do not compress files smaller than 256 bytesgzip_proxied any— compress responses from upstream even when they includeX-Accel-Expiresheaders
Brotli (ngx_brotli module)
Brotli provides better compression than gzip, but requires a separate module:
# Ubuntu: модуль из репозитория
sudo apt install libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-staticbrotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript;Check for Brotli support with the HTTP/2 & Brotli Checker.
Static file caching
Cache-Control headers
location ~* \.(jpg|jpeg|png|gif|ico|webp|avif|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(css|js)$ {
expires 30d;
add_header Cache-Control "public";
access_log off;
}
location ~* \.(woff2?|ttf|otf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}immutable — the browser will not revalidate the resource until the expiration time. Suitable for files with a hash in the filename (for example, main.a1b2c3.js).
Nginx-side caching (proxy_cache)
For responses from the backend:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
location / {
proxy_pass http://backend;
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
proxy_cache_background_update on;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
}
}Settings:
keys_zone=app_cache:10m— 10 MB for cache keysmax_size=1g— a maximum of 1 GB on diskinactive=60m— remove unused entries after 60 minutesproxy_cache_use_stale— serve stale cache when the backend returns errorsproxy_cache_lock— one request updates the cache while the others wait
Buffers and timeouts
Proxy buffers
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
proxy_max_temp_file_size 2048m;
proxy_temp_file_write_size 16k;When the backend returns a large response, Nginx buffers it. Insufficient buffer size leads to writes to temporary files and increased I/O.
Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;For long-running operations (file uploads, long polling), increase proxy_read_timeout.
Client buffers
client_body_buffer_size 16k;
client_header_buffer_size 1k;
client_max_body_size 10m;
large_client_header_buffers 4 8k;client_max_body_size — the limit on request body size (uploads). The default is 1 MB.
Keepalive to upstream
Persistent connections to the backend reduce the overhead of TCP/TLS handshakes:
upstream backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 32;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}proxy_http_version 1.1 and Connection "" are required for keepalive. keepalive 32 — a pool of 32 connections per worker.
Rate limiting (limiting request frequency)
Protection against overload and DDoS:
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
location / {
limit_req zone=general burst=20 nodelay;
limit_conn conn 10;
# ...
}
location /login {
limit_req zone=login burst=5 nodelay;
# ...
}
}rate=10r/s— 10 requests per second from one IPburst=20— allows a brief burst of up to 20 requestsnodelay— process the burst without delay (otherwise, excess requests are delayed)limit_conn conn 10— no more than 10 simultaneous connections from one IP
Load balancing
Round-robin (default)
upstream backend {
server 192.168.1.10:80;
server 192.168.1.11:80;
server 192.168.1.12:80;
}Weighted
upstream backend {
server 192.168.1.10:80 weight=3;
server 192.168.1.11:80 weight=2;
server 192.168.1.12:80 weight=1;
}Least connections
upstream backend {
least_conn;
server 192.168.1.10:80;
server 192.168.1.11:80;
}IP hash (sticky sessions)
upstream backend {
ip_hash;
server 192.168.1.10:80;
server 192.168.1.11:80;
}The same client is always routed to the same backend.
Health checks (commercial version)
Open-source Nginx does not include built-in health checks. Use max_fails and fail_timeout:
upstream backend {
server 192.168.1.10:80 max_fails=3 fail_timeout=30s;
server 192.168.1.11:80 max_fails=3 fail_timeout=30s;
}After 3 failed attempts, the server is excluded for 30 seconds.
Optimizing static file delivery
sendfile
sendfile on;
tcp_nopush on;
tcp_nodelay on;sendfile transfers files directly from the kernel without copying them to user space. tcp_nopush sends packets in full. tcp_nodelay disables Nagle's algorithm for faster delivery of small responses.
Open file cache
Caching open file descriptors:
open_file_cache max=10000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;SSL optimization
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_buffer_size 4k;ssl_buffer_size 4k reduces the size of the first TLS packet and can lower latency (TLS 1.3).
Logging
On high-traffic websites, logs create a significant load:
# Отключить access_log для статики
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
access_log off;
# ...
}
# Буферизация логов
access_log /var/log/nginx/access.log combined buffer=64k flush=5s;Complete example of an optimized nginx.conf
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/javascript application/xml image/svg+xml;
# File cache
open_file_cache max=10000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Rate limiting
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}Performance monitoring
After making changes, check:
- CPU and memory usage —
top,htop - Number of connections —
ss -s,netstat - Page load metrics — Web Vitals
- HTTP/2 and Brotli support — HTTP/2 Checker
Related articles
For more on configuration structure, see the Nginx guide. For SSL and TLS configuration, see the article on SSL in Nginx.