Skip to main content

Nginx Optimization on a VPS: Performance and Caching

Nginx · 19.04.2026 · 2 min read
Illustration for “Nginx Optimization on a VPS: Performance and Caching”

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).
Was this article helpful?
← Back to Knowledge Base Ask Support