Nginx cheat sheet
A scannable Nginx reference: 15 short snippets across 9 topics, each linking back to the lesson it came from.
At a glance
| Topic | What it covers | |
|---|---|---|
| Server blocks and static files | Configuration is a tree. http holds global settings, each server block is a virtual host, and location blocks decide | lesson |
| Reverse proxy and TLS | Without the forwarded headers the application sees nginx as the client, believes every request arrived over plain HTTP | lesson |
| Caching, compression and traps | The cache key decides correctness. The query string is already part of $request_uri; exclude cookies and headers you do | lesson |
| Installing nginx and understanding the configuration layout | The sites-available and sites-enabled convention is a Debian and Ubuntu packaging choice, not an nginx feature. On | lesson |
| How nginx matches a request: locations and rewrite phases | Work top down: the server block by server_name and listen port, then the location by the precedence rules, then the | lesson |
| Load balancing and upstreams | proxy_next_upstream retries on another node, but only for requests nginx knows are safe to repeat. A non-idempotent | lesson |
| WebSockets, gRPC and streaming responses | Upgrade headers for long-lived sockets, timeouts and buffering for streaming, gRPC proxying with HTTP/2, and disabling | lesson |
| Logging, metrics and debugging a config | Custom log formats, structured JSON access logs, error log levels, request IDs, stub_status metrics, and isolating a | lesson |
| Performance tuning: workers, buffers and timeouts | Worker processes and connections, sendfile and tcp_nopush, buffer sizing, keepalive timeouts, file descriptor limits | lesson |
Quick snippets
Server blocks and static files
Test and reload
sudo nginx -t # parse the config, report the first error
sudo nginx -T | less # dump the fully expanded config
sudo nginx -s reload # graceful: new workers, old ones drain
sudo systemctl reload nginx # the same thing under systemdFull lesson: Server blocks and static files →
Reverse proxy and TLS
Proxying an application
location / {
proxy_pass http://127.0.0.1:3000; # no trailing slash: URI passes through
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; # websockets
proxy_read_timeout 60s;
}
Upstream pools
upstream app {
least_conn; # or round-robin, or ip_hash
server 10.0.0.11:3000 max_fails=3 fail_timeout=15s;
server 10.0.0.12:3000 max_fails=3 fail_timeout=15s;
keepalive 32; # reuse upstream connections
}
location /api/ {
proxy_pass http://app;
proxy_next_upstream error timeout http_502 http_503;
}
TLS termination
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run # certificates last 90 days; automate renewalFull lesson: Reverse proxy and TLS →
Caching, compression and traps
Compression
gzip on;
gzip_comp_level 5; # 5 is the sweet spot; 9 costs CPU for little gain
gzip_min_length 1024; # do not compress tiny responses
gzip_vary on; # add Vary: Accept-Encoding for shared caches
gzip_types text/plain text/css application/json
application/javascript text/xml image/svg+xml;
# serve pre-compressed .gz files instead of compressing per request
gzip_static on;Full lesson: Caching, compression and traps →
Installing nginx and understanding the configuration layout
Installing and finding the files
# Debian and Ubuntu: the official repo has a newer build than the distribution
sudo apt install nginx
nginx -v # the version
nginx -V # version plus every compile-time module and prefix
# RHEL family
sudo dnf install nginx
# inspect what the running binary actually loads
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'prefix|conf-path|modules-path'
Testing and reloading safely
sudo nginx -t # parse and test the whole config
sudo nginx -t -c /etc/nginx/nginx.conf
sudo nginx -T # dump the fully expanded config with includes
sudo systemctl reload nginx # graceful: old workers finish, new ones start
sudo nginx -s reload # the same via a signal
sudo nginx -s quit # graceful shutdown
# never do this on a busy server
sudo systemctl restart nginxFull lesson: Installing nginx and understanding the configuration layout →
How nginx matches a request: locations and rewrite phases
The request phases in order
# return ends the request immediately, in the rewrite phase,
# before access controls run. It is cheap and cannot be bypassed by a file.
location = /old-page {
return 301 https://example.com/new-page;
}
# an internal rewrite re-runs location matching, which is why it costs more
location /legacy/ {
rewrite ^/legacy/(.*)$ /new/$1 last;
}
Reading an unfamiliar config
# 1. what is the fully expanded config, in order?
nginx -T > /tmp/expanded.conf
# 2. which server handles this host?
grep -n "server_name" /tmp/expanded.conf
# 3. which location matches this URI? add a marker and reload
# location = /debug-match { return 200 "matched exact\n"; }
# 4. test with a real request and inspect the result
curl -sSI https://example.com/static/app.css | head -20
curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://example.com/api/healthFull lesson: How nginx matches a request: locations and rewrite phases →
Load balancing and upstreams
Deploying and draining safely
# drain: keep serving existing requests but send new ones elsewhere.
# Mark the node down and reload; in-flight requests are not interrupted by a reload.
upstream app {
server 10.0.0.11:8080 down;
server 10.0.0.12:8080;
}
# then, after the connections have drained, deploy that node
# nginx -s reload -> graceful handover, old workers finish their requestsFull lesson: Load balancing and upstreams →
WebSockets, gRPC and streaming responses
Server-sent events and streaming responses
location /events/ {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off; # deliver each chunk as it is written
proxy_cache off;
proxy_read_timeout 24h;
chunked_transfer_encoding on;
add_header X-Accel-Buffering no; # tell the app not to buffer either
add_header Cache-Control "no-cache";
}Full lesson: WebSockets, gRPC and streaming responses →
Logging, metrics and debugging a config
Error levels and request ids
# tail the error log while reproducing a problem
sudo tail -f /var/log/nginx/error.log
# correlate a single request across nginx and the app
curl -sS -H 'X-Request-Id: trace-1234' https://example.com/api/orders -o /dev/null
grep trace-1234 /var/log/nginx/access.log /var/log/app/app.log
# raise the level for one server only, then reload and revert
# server { error_log /var/log/nginx/debug.log info; }
Metrics and bisecting a config
location = /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
# the output tells you whether you have a capacity or a latency problem
# Active connections: 291
# server accepts handled requests
# 1203948 1203948 4093211
# Reading: 4 Writing: 18 Waiting: 269
Metrics and bisecting a config
# bisect: comment out includes until the problem disappears, then narrow
grep -n "include" /etc/nginx/nginx.conf
nginx -t && systemctl reload nginx
# confirm the parsed value of a directive rather than guessing
nginx -T | grep -A5 "server_name example.com"Full lesson: Logging, metrics and debugging a config →
Performance tuning: workers, buffers and timeouts
Measure, then change one thing
# quick load test with concurrency, against a static file first
ab -n 20000 -c 200 https://example.com/static/app.css
# then against the dynamic path
ab -n 2000 -c 50 https://example.com/api/health
# watch the error log and the dashboard during the run
tail -f /var/log/nginx/error.log &
curl -s http://127.0.0.1/nginx_statusFull lesson: Performance tuning: workers, buffers and timeouts →
FAQ
Is this Nginx cheat sheet free to use?
Where do the examples come from?
How do I go deeper than a cheat sheet?
Related cheat sheets
Git Linux Docker Kubernetes CI / CD Bash Scripting
Last refreshed 2026-09-27.