MeshWorld MeshWorld.
Cheatsheet Nginx DevOps Backend Security Developer Tools 8 min read

Nginx Cheat Sheet: Config, Routing, SSL & Performance

Jena
By Jena
| Updated: Apr 9, 2026

Quick reference tables

Core directives

DirectiveWhat it does
worker_processes auto;Set worker processes to match CPU cores
worker_connections 1024;Max connections per worker
keepalive_timeout 65;Keep-alive connection timeout in seconds
server_tokens off;Hide Nginx version in headers and error pages
include /etc/nginx/conf.d/*.conf;Load additional config files
include /etc/nginx/sites-enabled/*;Load site configs (Debian/Ubuntu layout)

Server block structure

DirectiveWhat it does
listen 80;Listen on port 80 (IPv4)
listen 80 default_server;Default server for unmatched requests
listen [::]:80;Listen on port 80 (IPv6)
server_name example.com www.example.com;Match these hostnames
server_name _;Catch-all server block
root /var/www/html;Document root directory
index index.html index.php;Default index files
charset utf-8;Set response charset

Location matching (priority order)

SyntaxMatch typePriority
location = /exactExact matchHighest
location ^~ /prefixPrefix match (stops regex search)High
location ~ \.php$Case-sensitive regexMedium
location ~* \.jpg$Case-insensitive regexMedium
location /prefixPrefix match (continues to check regex)Low
location /Catch-allLowest

Proxy pass (reverse proxy)

DirectiveWhat it does
proxy_pass http://localhost:3000;Forward to upstream server
proxy_pass http://localhost:3000/;Trailing slash strips the location prefix
proxy_http_version 1.1;Required for WebSocket and keep-alive
proxy_set_header Host $host;Pass original Host header
proxy_set_header X-Real-IP $remote_addr;Pass client IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;Append to X-Forwarded-For chain
proxy_set_header X-Forwarded-Proto $scheme;Pass http or https
proxy_set_header Upgrade $http_upgrade;Required for WebSocket
proxy_set_header Connection "upgrade";Required for WebSocket
proxy_buffering off;Disable buffering (needed for SSE/streaming)
proxy_read_timeout 60s;Upstream response timeout
proxy_connect_timeout 10s;Time to establish upstream connection

SSL / TLS

DirectiveWhat it does
listen 443 ssl;Listen on HTTPS
ssl_certificate /etc/ssl/cert.pem;Path to certificate (full chain)
ssl_certificate_key /etc/ssl/key.pem;Path to private key
ssl_protocols TLSv1.2 TLSv1.3;Only allow TLS 1.2 and 1.3
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:...;Allowed cipher suites
ssl_prefer_server_ciphers on;Prefer server’s cipher order
ssl_session_cache shared:SSL:10m;SSL session cache (10MB)
ssl_session_timeout 1d;SSL session reuse window
ssl_stapling on;Enable OCSP stapling
ssl_stapling_verify on;Verify OCSP responses

Redirects

DirectiveWhat it does
return 301 https://$host$request_uri;Permanent redirect (HTTP → HTTPS)
return 302 /new-path;Temporary redirect
rewrite ^/old$ /new permanent;Regex-based permanent redirect
rewrite ^/old(.*)$ /new$1 permanent;Redirect with path preservation

Headers

DirectiveWhat it does
add_header X-Frame-Options "DENY";Prevent clickjacking
add_header X-Content-Type-Options "nosniff";Prevent MIME sniffing
add_header X-XSS-Protection "1; mode=block";XSS protection (legacy)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;HSTS
add_header Referrer-Policy "strict-origin-when-cross-origin";Referrer policy
add_header Content-Security-Policy "default-src 'self'";CSP
add_header alwaysApply header even on error responses

Static file serving and caching

DirectiveWhat it does
expires 1y;Set Cache-Control and Expires headers
expires -1;Disable caching for this location
add_header Cache-Control "public, immutable";Immutable assets (hashed filenames)
etag on;Enable ETag generation
if_modified_since exact;Exact Last-Modified comparison
open_file_cache max=1000 inactive=20s;Cache open file descriptors
gzip on;Enable gzip compression
gzip_types text/plain text/css application/json application/javascript;MIME types to compress
gzip_min_length 1000;Don’t compress files smaller than 1KB
gzip_comp_level 6;Compression level 1–9 (6 is a good balance)

Rate limiting

DirectiveWhat it does
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;Define zone (in http block)
limit_req zone=api burst=20 nodelay;Apply limit in location block
limit_req_status 429;Return 429 Too Many Requests
limit_conn_zone $binary_remote_addr zone=conn:10m;Connection limit zone
limit_conn conn 10;Max concurrent connections per IP

Upstream (load balancing)

DirectiveWhat it does
upstream backend { ... }Define upstream group
server 127.0.0.1:3000;Add server to upstream
server 127.0.0.1:3001 weight=2;Weighted load balancing
server 127.0.0.1:3002 backup;Failover server
least_conn;Route to server with fewest active connections
ip_hash;Sticky sessions by client IP
keepalive 32;Keep persistent connections to upstream

Common CLI commands

CommandWhat it does
nginx -tTest config syntax (always run before reload)
nginx -TTest config and dump full compiled config
nginx -s reloadReload config without dropping connections
nginx -s stopFast shutdown
nginx -s quitGraceful shutdown
systemctl reload nginxReload via systemd
systemctl restart nginxFull restart via systemd
nginx -vShow version
nginx -VShow version and compile options

Detailed sections

Production-ready HTTPS server block

This is a complete, secure config for a Node.js/Python app behind Nginx. Replace example.com and the upstream port with your own values.

# Redirect all HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

# Main HTTPS server
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com www.example.com;

    # SSL
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_stapling        on;
    ssl_stapling_verify on;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    server_tokens off;

    # Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript application/xml;
    gzip_min_length 1000;

    # Proxy to Node.js app
    location / {
        proxy_pass         http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
    }

    # Static assets with long cache
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Block dotfiles
    location ~ /\. {
        deny all;
    }
}

Location block matching — the order matters

Nginx picks the most specific matching location. This is one of the most common sources of config bugs.

server {
    # 1. Exact match — highest priority
    location = /health {
        return 200 "OK";
    }

    # 2. Prefix match with regex-stop — second priority
    # Use ^~ when you want a prefix match that beats any regex
    location ^~ /static/ {
        root /var/www;
        expires 1y;
    }

    # 3. Regex matches — evaluated in order, first match wins
    location ~* \.(php|asp|aspx)$ {
        return 403;  # Block server-side scripts in wrong places
    }

    # 4. Catch-all prefix — lowest priority
    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

Rate limiting — protect your API

Define the zone in the http {} block, apply it in server {} or location {}.

http {
    # Zone: 10MB of state (stores ~160k unique IPs)
    # Rate: max 10 requests per second per IP
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req_status 429;

    server {
        location /api/ {
            # burst=20: allow up to 20 queued requests above the rate
            # nodelay: serve burst immediately instead of spacing them out
            limit_req zone=api burst=20 nodelay;
            proxy_pass http://127.0.0.1:3000;
        }
    }
}

WebSocket reverse proxy

WebSockets require two specific headers. Without them, the connection upgrade fails.

location /ws/ {
    proxy_pass         http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header   Upgrade $http_upgrade;
    proxy_set_header   Connection "upgrade";
    proxy_set_header   Host $host;
    proxy_read_timeout 3600s;  # Keep WS connections open longer
}

Certbot / Let’s Encrypt integration

Get a free certificate and auto-configure Nginx in one command. Requires the certbot and python3-certbot-nginx packages.

# Install certbot (Debian/Ubuntu)
sudo apt install certbot python3-certbot-nginx

# Get and install certificate
sudo certbot --nginx -d example.com -d www.example.com

# Test auto-renewal
sudo certbot renew --dry-run

Certbot edits your config in place, adding the SSL directives. After it runs, always check nginx -t before assuming the config is valid.

Related: Linux Bash Cheat Sheet | SSH & GPG Cheat Sheet | How to Set Up a Reverse Proxy with Nginx