Nginx works well out of the box, but not optimally. A few configuration changes multiply performance and reduce the load on the server.
nginx.conf: Basic Settings
worker_processes auto; # Auto = number of CPU cores
worker_rlimit_nofile 65535; # Max open files
events {
worker_connections 4096; # Connections per worker
use epoll; # Linux-optimized poll
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off; # Hide Nginx version
}
Gzip Compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_types
text/plain text/css text/javascript
application/javascript application/json
application/xml image/svg+xml;
Browser Caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
FastCGI Cache for PHP
# In the http block:
fastcgi_cache_path /var/cache/nginx levels=1:2
keys_zone=WORDPRESS:100m
inactive=60m max_size=1g;
# In the server block:
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_use_stale error timeout updating http_500;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
✅ FastCGI Cache for WordPress reduces TTFB from 300–500 ms to 5–20 ms for cached pages.
Rate Limiting (Flood Protection)
# In the http block:
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# In the location block:
location /wp-login.php {
limit_req zone=api burst=5 nodelay;
limit_req_status 429;
}
HTTP/2 and HTTP/3
server {
listen 443 ssl http2;
# HTTP/3 (QUIC):
listen 443 quic reuseport;
add_header Alt-Svc 'h3=":443"; ma=86400';
}
nginx.conf — key parameters
worker_processes auto; # = number of CPUs
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
keepalive_requests 1000;
types_hash_max_size 2048;
server_tokens off; # Hide Nginx version
# Gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json
application/javascript text/xml application/xml
image/svg+xml font/woff2;
}
HTTP/2 + SSL
server {
listen 443 ssl http2;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
}
Static file caching
location ~* \.(jpg|jpeg|png|webp|gif|ico|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
Rate limiting — protection against overload
http {
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
}
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_conn conn 10;
}
}
💡 After making changes:
nginx -t (check the config) → systemctl reload nginx (apply without restarting).