Nginx Cheat Sheet

A comprehensive quick-reference for Nginx configuration directives and CLI commands. Search by keyword or filter by category to find exactly what you need.

What is Nginx?

Nginx (pronounced "engine-x") is a high-performance HTTP server, reverse proxy, load balancer, and mail proxy. Originally created to solve the C10K problem, it uses an event-driven, asynchronous architecture that handles thousands of concurrent connections with minimal memory usage.

Key Capabilities

  • HTTP Server: Serves static and dynamic content
  • Reverse Proxy: Forwards requests to app servers
  • Load Balancer: Distributes traffic across backends
  • SSL Termination: Handles HTTPS and TLS offloading
  • Rate Limiting: Protects backends from abuse
  • Caching: Caches upstream responses

Common Use Cases

  • Front-end server for Node.js, Python, PHP apps
  • Static file serving with aggressive caching
  • API gateway and reverse proxy
  • Load balancing across multiple app instances
  • SSL/TLS termination with Let's Encrypt
  • Microservice ingress controller

Configuration File Structure

Main config:

  • /etc/nginx/nginx.conf
  • Global settings and http block
  • Includes site configs

Site configs:

  • sites-available/
  • sites-enabled/
  • conf.d/

Logs:

  • /var/log/nginx/access.log
  • /var/log/nginx/error.log
32 directives found
Filter:

Server Block

Basic Config

The fundamental unit of Nginx configuration that defines a virtual host

Syntax:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.html index.htm;
}

Examples:

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example;
    index index.html index.php;
}
Basic HTTP server block for a domain
server {
    listen 80 default_server;
    server_name _;
    return 444;
}
Default catch-all server that closes connections for unknown hosts
server {
    listen [::]:80 ipv6only=on;
    server_name example.com;
    root /var/www/html;
}
IPv6-only server block

Notes:

Nginx selects the server block using the Host header. Use default_server to handle requests that do not match any server_name.

listen

Basic Config

Defines the IP address and port the server listens on

Syntax:

listen address[:port] [ssl] [http2] [default_server];

Examples:

listen 80;
Listen on port 80 (all interfaces)
listen 443 ssl;
Listen on port 443 with SSL
listen 443 ssl http2;
Listen on port 443 with SSL and HTTP/2
listen 127.0.0.1:8080;
Listen on localhost port 8080 only
listen 80 default_server;
Mark as the default server for this port

Notes:

http2 requires ssl to be specified. For HTTP/3 (QUIC), use listen 443 quic reuseport (Nginx 1.25+).

server_name

Basic Config

Defines which hostnames this server block handles

Syntax:

server_name name [name ...];

Examples:

server_name example.com;
Exact match for one domain
server_name example.com www.example.com;
Multiple exact names
server_name *.example.com;
Wildcard for all subdomains
server_name ~^(www\.)?(?P.+)$;
Regex match with named capture group
server_name _;
Catch-all (matches any hostname)

Notes:

Nginx checks exact names first, then wildcards, then regex patterns. The _ catch-all is not a special character — it just never matches a real hostname.

root / index

Basic Config

Set the document root and default index files

Syntax:

root /path/to/files;
index index.html index.php;

Examples:

server {
    root /var/www/html;
    index index.html index.htm;
}
Serve files from /var/www/html
location /app {
    root /var/www;
    # Serves files from /var/www/app/
}
root appends the location path
location /app {
    alias /var/www/static/;
    # Serves files from /var/www/static/ directly
}
alias replaces the location path entirely

Notes:

root appends the URI to the path (location /app → /var/www/html/app). alias replaces the location path. Use alias inside location blocks to avoid confusion.

Location Matching

Location Blocks

Controls how URIs are matched and in what priority order

Syntax:

location [modifier] uri { ... }

Examples:

location = /exact { }
Exact match — highest priority, stops searching
location ^~ /prefix/ { }
Prefix match — stops regex search if matched
location ~* \.(jpg|png|gif)$ { }
Case-insensitive regex match
location ~ \.php$ { }
Case-sensitive regex match
location /prefix/ { }
Prefix match — lowest priority among prefix types

Notes:

Priority order: (1) exact = (2) prefix ^~ (3) regex ~ or ~* (first match wins) (4) longest prefix. Use = for single URIs and ^~ to block regex for a prefix.

Common Location Patterns

Location Blocks

Practical location block configurations for common use cases

Syntax:

location /path { directives; }

Examples:

location / {
    try_files $uri $uri/ /index.html;
}
SPA fallback — serve index.html for all routes
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}
Cache static assets for 1 year
location ~ /\.ht {
    deny all;
}
Block access to .htaccess files
location = /health {
    access_log off;
    return 200 "OK";
    add_header Content-Type text/plain;
}
Health check endpoint without logging

try_files

Location Blocks

Tries each path in order and serves the first one that exists

Syntax:

try_files file ... uri;
try_files file ... =code;

Examples:

try_files $uri $uri/ =404;
Try file, then directory, then return 404
try_files $uri $uri/ /index.php?$query_string;
PHP app — fall through to index.php
try_files $uri $uri/ /index.html;
SPA — always serve index.html as fallback
try_files /cache/$uri $uri @backend;
Serve from cache if exists, else proxy to backend

Notes:

The last argument is always a fallback — either a URI (internal redirect) or an error code with =. Named locations (@name) can be used as fallback.

proxy_pass

Proxy & Upstream

Forward requests to a backend server or upstream group

Syntax:

proxy_pass http://backend_address;

Examples:

location /api {
    proxy_pass http://127.0.0.1:3000;
}
Proxy /api to a local Node.js app on port 3000
location /api/ {
    proxy_pass http://127.0.0.1:3000/;
}
Strip /api/ prefix before forwarding (note trailing slashes)
location / {
    proxy_pass http://myapp;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}
WebSocket proxy with connection upgrade

Notes:

Trailing slash behavior is tricky. With proxy_pass http://host, the full URI is forwarded. With proxy_pass http://host/, the location prefix is stripped.

Proxy Headers

Proxy & Upstream

Set headers to pass client information to the backend

Syntax:

proxy_set_header HeaderName value;

Examples:

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;
Standard proxy headers — put in a shared include file
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
Remove sensitive headers from backend responses
proxy_read_timeout 300s;
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
Increase timeouts for slow backends

Notes:

Always set X-Forwarded-For and X-Forwarded-Proto so your app can detect the real client IP and whether the request was HTTP or HTTPS.

upstream

Proxy & Upstream

Define a group of backend servers for load balancing

Syntax:

upstream name { server address [params]; }

Examples:

upstream myapp {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

server {
    location / {
        proxy_pass http://myapp;
    }
}
Round-robin load balancing across 3 servers (default)
upstream myapp {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}
Least connections load balancing
upstream myapp {
    ip_hash;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}
IP hash — sticky sessions by client IP
upstream myapp {
    server 10.0.0.1:8080 weight=3;
    server 10.0.0.2:8080 weight=1;
    server 10.0.0.3:8080 backup;
}
Weighted round-robin with a backup server

Notes:

Nginx Plus adds advanced options like zone, sticky cookie, and health checks. Open-source Nginx uses passive health checks only.

SSL Certificate Setup

SSL/TLS

Configure HTTPS with SSL certificate and key

Syntax:

ssl_certificate /path/cert.pem;
ssl_certificate_key /path/key.pem;

Examples:

server {
    listen 443 ssl;
    server_name example.com;

    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_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
}
HTTPS server with Let's Encrypt certificate
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
SSL session caching for better performance

Notes:

Use certbot to obtain free Let's Encrypt certificates. Always disable SSLv2, SSLv3, and TLSv1.0/1.1 for security.

HTTP to HTTPS Redirect

SSL/TLS

Redirect all HTTP traffic to HTTPS

Syntax:

return 301 https://$host$request_uri;

Examples:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}
Permanent redirect from HTTP to HTTPS
server {
    listen 80;
    server_name example.com;
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
    location / {
        return 301 https://$host$request_uri;
    }
}
Allow ACME challenge for cert renewal, redirect everything else

Notes:

Use 301 for production (permanent). Use 302 during testing so browsers do not cache the redirect while you are still configuring.

HSTS

SSL/TLS

HTTP Strict Transport Security — force browsers to use HTTPS

Syntax:

add_header Strict-Transport-Security "max-age=seconds [; includeSubDomains] [; preload]";

Examples:

add_header Strict-Transport-Security "max-age=31536000" always;
HSTS for 1 year
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
HSTS with subdomains and preload list submission

Notes:

Only add HSTS after you are certain HTTPS is fully working. The preload directive requires submitting to hstspreload.org and is very difficult to undo.

expires / Cache Headers

Static Files

Control browser caching for static assets

Syntax:

expires duration;
add_header Cache-Control "directive";

Examples:

location ~* \.(css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}
Cache versioned CSS and JS files for 1 year
location ~* \.(jpg|jpeg|png|gif|webp|ico|svg)$ {
    expires 30d;
    add_header Cache-Control "public, max-age=2592000";
}
Cache images for 30 days
location / {
    expires -1;
    add_header Cache-Control "no-store, no-cache, must-revalidate";
}
Disable caching for dynamic pages

Notes:

Use immutable only for files with content-hashed filenames (e.g. app.abc123.js). Do not use it for files that may change at the same URL.

gzip Compression

Static Files

Enable gzip compression to reduce response sizes

Syntax:

gzip on;
gzip_types mime/type ...;

Examples:

gzip on;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_proxied any;
gzip_types
    text/plain
    text/css
    text/javascript
    application/javascript
    application/json
    application/xml
    image/svg+xml;
Recommended gzip configuration
gzip_vary on;
Add Vary: Accept-Encoding header for CDN compatibility

Notes:

gzip_comp_level 6 is a good balance between compression ratio and CPU usage. Level 9 gives minimal additional benefit with significantly more CPU. Do not compress already-compressed formats like .jpg, .zip, .gz.

allow / deny

Security

Restrict access by IP address

Syntax:

allow address;
deny address;
deny all;

Examples:

location /admin {
    allow 192.168.1.0/24;
    allow 10.0.0.1;
    deny all;
}
Allow only specific IPs to access /admin
location /metrics {
    allow 127.0.0.1;
    deny all;
}
Restrict metrics endpoint to localhost

Notes:

Rules are evaluated in order — first match wins. Always put deny all at the end of an allow list.

Rate Limiting

Security

Limit request rates to protect against abuse and DDoS

Syntax:

limit_req_zone key zone=name:size rate=n r/s;
limit_req zone=name [burst=n] [nodelay];

Examples:

http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

    server {
        location /api/ {
            limit_req zone=api burst=20 nodelay;
        }
    }
}
10 req/s per IP with burst of 20, immediate rejection after burst
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

location /login {
    limit_req zone=login burst=3;
    limit_req_status 429;
}
Strict rate limit for login endpoint: 5 per minute

Notes:

Use $binary_remote_addr instead of $remote_addr — it uses less memory. The zone size 10m can store ~160,000 IP states. nodelay allows burst requests immediately without delay.

Security Headers

Security

Add HTTP security headers to protect against common attacks

Syntax:

add_header HeaderName "value" always;

Examples:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
Comprehensive security headers set
server_tokens off;
Hide Nginx version from Server response header

Notes:

The always keyword ensures headers are sent even for error responses. server_tokens off hides the Nginx version from response headers and error pages.

return

Rewrites & Redirects

Stop processing and return a status code or redirect URL

Syntax:

return code [URL];
return code [text];

Examples:

return 301 https://example.com$request_uri;
Permanent redirect
return 302 /new-path;
Temporary redirect
return 404;
Return 404 Not Found
return 200 "healthy";
Return plain text response
return 301 https://www.$host$request_uri;
Redirect non-www to www

Notes:

Prefer return over rewrite for simple redirects — it is faster and cleaner. Use rewrite only when you need to capture groups from the URI.

rewrite

Rewrites & Redirects

Modify the request URI using regex and replacement

Syntax:

rewrite regex replacement [flag];

Examples:

rewrite ^/old-page$ /new-page permanent;
Redirect old URL to new URL (301)
rewrite ^/blog/(.*)$ /articles/$1 last;
Rewrite URL internally — capture group forwarded
rewrite ^/api/v1/(.*)$ /api/v2/$1 break;
Rewrite without searching other location blocks (break)

Notes:

Flags: last — restart location search with new URI. break — stop rewrite processing. redirect — 302. permanent — 301. Use last inside server blocks, break inside location blocks.

Named Locations

Rewrites & Redirects

Define an internal-only location that can be used as a fallback

Syntax:

location @name { ... }

Examples:

location / {
    try_files $uri $uri/ @backend;
}

location @backend {
    proxy_pass http://127.0.0.1:3000;
}
Try to serve static file, fall back to Node.js app

Notes:

Named locations start with @. They cannot be accessed by external clients — only used as fallbacks in try_files or error_page directives.

access_log

Logging

Configure where and how access logs are written

Syntax:

access_log path [format] [buffer=size] [flush=time];
access_log off;

Examples:

access_log /var/log/nginx/access.log;
Default access log location
access_log /var/log/nginx/app.log combined;
Use named log format
access_log off;
Disable access logging for this context
location /health {
    access_log off;
    return 200 "OK";
}
Disable logging for health check endpoint

error_log

Logging

Configure error logging with severity level

Syntax:

error_log file [level];

Examples:

error_log /var/log/nginx/error.log warn;
Log warnings and above
error_log /var/log/nginx/error.log debug;
Verbose debug logging (development only)
error_log /dev/null;
Discard all error logs

Notes:

Log levels (lowest to highest): debug, info, notice, warn, error, crit, alert, emerg. Each level includes all higher-severity levels.

log_format

Logging

Define custom log formats for structured or enhanced logging

Syntax:

log_format name [escape=default|json|none] string;

Examples:

log_format main '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $body_bytes_sent '
                       '"$http_referer" "$http_user_agent"';
Default combined log format
log_format json_combined escape=json
    '{'
        '"time": "$time_iso8601",'
        '"ip": "$remote_addr",'
        '"method": "$request_method",'
        '"uri": "$request_uri",'
        '"status": $status,'
        '"bytes": $body_bytes_sent,'
        '"duration": $request_time,'
        '"ua": "$http_user_agent"'
    '}';
JSON structured log format for log aggregation systems

Notes:

JSON log formats work well with Elasticsearch, Loki, and other log aggregators. Use escape=json to properly escape special characters.

worker_processes

Performance

Set the number of worker processes Nginx spawns

Syntax:

worker_processes number | auto;

Examples:

worker_processes auto;
Auto-detect based on CPU cores (recommended)
worker_processes 4;
Explicitly set 4 worker processes
worker_processes auto;
worker_rlimit_nofile 65535;
Auto workers with increased file descriptor limit

Notes:

Set to auto in almost all cases. One worker per CPU core is ideal for CPU-bound workloads. For I/O-bound (most web servers), auto is fine.

keepalive

Performance

Configure persistent connections to reduce connection overhead

Syntax:

keepalive_timeout seconds;
keepalive_requests number;

Examples:

keepalive_timeout 65;
keepalive_requests 1000;
Keep connections alive for 65s, up to 1000 requests
upstream backend {
    server 127.0.0.1:3000;
    keepalive 32;
}
Keep 32 idle keepalive connections to upstream

Notes:

Upstream keepalive (keepalive in upstream block) reuses backend connections and greatly reduces connection overhead for high-traffic sites.

sendfile / tcp_nopush

Performance

Optimize file transfer performance with OS-level optimizations

Syntax:

sendfile on;
tcp_nopush on;
tcp_nodelay on;

Examples:

sendfile on;
tcp_nopush on;
tcp_nodelay on;
Recommended performance trio for static file serving
sendfile on;
aio on;
directio 512;
Asynchronous I/O for large files

Notes:

sendfile uses the OS sendfile() syscall to send files without copying to userspace. tcp_nopush batches data in full TCP packets. tcp_nodelay disables Nagle algorithm for low-latency responses.

worker_connections

Performance

Maximum simultaneous connections per worker process

Syntax:

worker_connections number;

Examples:

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}
Recommended events block with epoll (Linux)
# Max connections = worker_processes * worker_connections
# For proxy: max_clients = worker_processes * worker_connections / 4
Calculation for maximum client connections

Notes:

epoll (Linux) and kqueue (BSD/macOS) are the most efficient event models. multi_accept allows a worker to accept all new connections at once instead of one at a time.

nginx -t

CLI Commands

Test configuration syntax without restarting Nginx

Syntax:

nginx -t
nginx -T

Examples:

nginx -t
Test configuration and report errors
nginx -T
Test and dump entire parsed configuration
nginx -t -c /path/to/nginx.conf
Test a specific config file

Notes:

Always run nginx -t before reloading or restarting. A failed test will prevent the reload but will NOT bring down a currently running Nginx instance.

nginx -s signal

CLI Commands

Send control signals to a running Nginx master process

Syntax:

nginx -s signal

Examples:

nginx -s reload
Gracefully reload config without dropping connections
nginx -s stop
Fast shutdown (immediate)
nginx -s quit
Graceful shutdown (waits for active connections to finish)
nginx -s reopen
Reopen log files (use after log rotation)

Notes:

Prefer reload over restart — it applies new config without any downtime. Workers serving active requests continue with old config until the request completes.

systemctl

CLI Commands

Manage Nginx as a systemd service

Syntax:

systemctl [start|stop|restart|reload|status|enable] nginx

Examples:

systemctl start nginx
Start Nginx service
systemctl stop nginx
Stop Nginx service
systemctl restart nginx
Restart Nginx (brief downtime)
systemctl reload nginx
Graceful reload (no downtime)
systemctl status nginx
Show service status and recent logs
systemctl enable nginx
Enable Nginx to start on boot
journalctl -u nginx -f
Follow Nginx service logs in real time

Debugging & Inspection

CLI Commands

Useful commands for inspecting and debugging Nginx configuration and logs

Syntax:

nginx -V
nginx -v

Examples:

nginx -v
Show Nginx version
nginx -V
Show version, compiler flags, and built-in modules
tail -f /var/log/nginx/error.log
Watch error log in real time
tail -f /var/log/nginx/access.log
Watch access log in real time
nginx -g "daemon off;"
Run Nginx in foreground (useful for Docker)
curl -I http://localhost
Check response headers from Nginx

Notes:

nginx -V shows which modules are compiled in. If a module you need is not listed (e.g., --with-http_stub_status_module), you need to recompile or install a different package.

Nginx Best Practices

Performance

  • Set worker_processes auto to match CPU cores
  • Enable sendfile on and tcp_nopush on for static files
  • Enable gzip compression for text-based responses
  • Use upstream keepalive to reuse backend connections
  • Set long expires headers for versioned static assets
  • Use open_file_cache to cache frequently opened files

Security

  • Always run nginx -t before reloading
  • Use server_tokens off to hide the Nginx version
  • Restrict sensitive paths with deny all
  • Add rate limiting to login and API endpoints
  • Use TLSv1.2 and TLSv1.3 only — disable older protocols
  • Add security headers: CSP, X-Frame-Options, HSTS