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
Server Block
Basic ConfigThe 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 domainserver {
listen 80 default_server;
server_name _;
return 444;
} Default catch-all server that closes connections for unknown hostsserver {
listen [::]:80 ipv6only=on;
server_name example.com;
root /var/www/html;
} IPv6-only server blockNotes:
Nginx selects the server block using the Host header. Use default_server to handle requests that do not match any server_name.
listen
Basic ConfigDefines 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 SSLlisten 443 ssl http2; Listen on port 443 with SSL and HTTP/2listen 127.0.0.1:8080; Listen on localhost port 8080 onlylisten 80 default_server; Mark as the default server for this portNotes:
http2 requires ssl to be specified. For HTTP/3 (QUIC), use listen 443 quic reuseport (Nginx 1.25+).
server_name
Basic ConfigDefines which hostnames this server block handles
Syntax:
server_name name [name ...];Examples:
server_name example.com; Exact match for one domainserver_name example.com www.example.com; Multiple exact namesserver_name *.example.com; Wildcard for all subdomainsserver_name ~^(www\.)?(?P.+)$; Regex match with named capture groupserver_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 ConfigSet 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/htmllocation /app {
root /var/www;
# Serves files from /var/www/app/
} root appends the location pathlocation /app {
alias /var/www/static/;
# Serves files from /var/www/static/ directly
} alias replaces the location path entirelyNotes:
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 BlocksControls how URIs are matched and in what priority order
Syntax:
location [modifier] uri { ... }Examples:
location = /exact { } Exact match — highest priority, stops searchinglocation ^~ /prefix/ { } Prefix match — stops regex search if matchedlocation ~* \.(jpg|png|gif)$ { } Case-insensitive regex matchlocation ~ \.php$ { } Case-sensitive regex matchlocation /prefix/ { } Prefix match — lowest priority among prefix typesNotes:
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 BlocksPractical 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 routeslocation ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
} Cache static assets for 1 yearlocation ~ /\.ht {
deny all;
} Block access to .htaccess fileslocation = /health {
access_log off;
return 200 "OK";
add_header Content-Type text/plain;
} Health check endpoint without loggingtry_files
Location BlocksTries 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 404try_files $uri $uri/ /index.php?$query_string; PHP app — fall through to index.phptry_files $uri $uri/ /index.html; SPA — always serve index.html as fallbacktry_files /cache/$uri $uri @backend; Serve from cache if exists, else proxy to backendNotes:
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 & UpstreamForward 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 3000location /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 upgradeNotes:
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 & UpstreamSet 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 fileproxy_hide_header X-Powered-By;
proxy_hide_header Server; Remove sensitive headers from backend responsesproxy_read_timeout 300s;
proxy_connect_timeout 10s;
proxy_send_timeout 300s; Increase timeouts for slow backendsNotes:
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 & UpstreamDefine 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 balancingupstream myapp {
ip_hash;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
} IP hash — sticky sessions by client IPupstream 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 serverNotes:
Nginx Plus adds advanced options like zone, sticky cookie, and health checks. Open-source Nginx uses passive health checks only.
SSL Certificate Setup
SSL/TLSConfigure 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 certificatessl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off; SSL session caching for better performanceNotes:
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/TLSRedirect 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 HTTPSserver {
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 elseNotes:
Use 301 for production (permanent). Use 302 during testing so browsers do not cache the redirect while you are still configuring.
HSTS
SSL/TLSHTTP 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 yearadd_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; HSTS with subdomains and preload list submissionNotes:
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 FilesControl 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 yearlocation ~* \.(jpg|jpeg|png|gif|webp|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
} Cache images for 30 dayslocation / {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
} Disable caching for dynamic pagesNotes:
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 FilesEnable 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 configurationgzip_vary on; Add Vary: Accept-Encoding header for CDN compatibilityNotes:
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
SecurityRestrict 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 /adminlocation /metrics {
allow 127.0.0.1;
deny all;
} Restrict metrics endpoint to localhostNotes:
Rules are evaluated in order — first match wins. Always put deny all at the end of an allow list.
Rate Limiting
SecurityLimit 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 burstlimit_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 minuteNotes:
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
SecurityAdd 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 setserver_tokens off; Hide Nginx version from Server response headerNotes:
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 & RedirectsStop 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 redirectreturn 302 /new-path; Temporary redirectreturn 404; Return 404 Not Foundreturn 200 "healthy"; Return plain text responsereturn 301 https://www.$host$request_uri; Redirect non-www to wwwNotes:
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 & RedirectsModify 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 forwardedrewrite ^/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 & RedirectsDefine 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 appNotes:
Named locations start with @. They cannot be accessed by external clients — only used as fallbacks in try_files or error_page directives.
access_log
LoggingConfigure 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 locationaccess_log /var/log/nginx/app.log combined; Use named log formataccess_log off; Disable access logging for this contextlocation /health {
access_log off;
return 200 "OK";
} Disable logging for health check endpointerror_log
LoggingConfigure error logging with severity level
Syntax:
error_log file [level];Examples:
error_log /var/log/nginx/error.log warn; Log warnings and aboveerror_log /var/log/nginx/error.log debug; Verbose debug logging (development only)error_log /dev/null; Discard all error logsNotes:
Log levels (lowest to highest): debug, info, notice, warn, error, crit, alert, emerg. Each level includes all higher-severity levels.
log_format
LoggingDefine 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 formatlog_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 systemsNotes:
JSON log formats work well with Elasticsearch, Loki, and other log aggregators. Use escape=json to properly escape special characters.
worker_processes
PerformanceSet 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 processesworker_processes auto;
worker_rlimit_nofile 65535; Auto workers with increased file descriptor limitNotes:
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
PerformanceConfigure 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 requestsupstream backend {
server 127.0.0.1:3000;
keepalive 32;
} Keep 32 idle keepalive connections to upstreamNotes:
Upstream keepalive (keepalive in upstream block) reuses backend connections and greatly reduces connection overhead for high-traffic sites.
sendfile / tcp_nopush
PerformanceOptimize 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 servingsendfile on;
aio on;
directio 512; Asynchronous I/O for large filesNotes:
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
PerformanceMaximum 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 connectionsNotes:
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 CommandsTest configuration syntax without restarting Nginx
Syntax:
nginx -t
nginx -TExamples:
nginx -t Test configuration and report errorsnginx -T Test and dump entire parsed configurationnginx -t -c /path/to/nginx.conf Test a specific config fileNotes:
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 CommandsSend control signals to a running Nginx master process
Syntax:
nginx -s signalExamples:
nginx -s reload Gracefully reload config without dropping connectionsnginx -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 CommandsManage Nginx as a systemd service
Syntax:
systemctl [start|stop|restart|reload|status|enable] nginxExamples:
systemctl start nginx Start Nginx servicesystemctl stop nginx Stop Nginx servicesystemctl restart nginx Restart Nginx (brief downtime)systemctl reload nginx Graceful reload (no downtime)systemctl status nginx Show service status and recent logssystemctl enable nginx Enable Nginx to start on bootjournalctl -u nginx -f Follow Nginx service logs in real timeDebugging & Inspection
CLI CommandsUseful commands for inspecting and debugging Nginx configuration and logs
Syntax:
nginx -V
nginx -vExamples:
nginx -v Show Nginx versionnginx -V Show version, compiler flags, and built-in modulestail -f /var/log/nginx/error.log Watch error log in real timetail -f /var/log/nginx/access.log Watch access log in real timenginx -g "daemon off;" Run Nginx in foreground (useful for Docker)curl -I http://localhost Check response headers from NginxNotes:
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 autoto match CPU cores - Enable
sendfile onandtcp_nopush onfor static files - Enable gzip compression for text-based responses
- Use upstream
keepaliveto reuse backend connections - Set long
expiresheaders for versioned static assets - Use
open_file_cacheto cache frequently opened files
Security
- Always run
nginx -tbefore reloading - Use
server_tokens offto 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