Nginx Cheatsheet
SSL/TLS
Use this Nginx reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Minimal HTTPS Server
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
location / {
root /var/www/html;
}
}HTTP → HTTPS Redirect
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}Recommended TLS Config
server {
listen 443 ssl;
http2 on; # Enable HTTP/2 (Nginx 1.25.1+)
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 ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off; # Let client pick from the list
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m; # ~40k sessions
ssl_session_tickets off; # Forward secrecy
}No OCSP stapling: Let's Encrypt shut down its OCSP responders in 2025 (revocation moved to CRLs), so
ssl_stapling onis dead config on LE certs. Only enable stapling if your CA still runs an OCSP responder.
HTTP/3 (QUIC)
Built in since nginx 1.25 (use current stable, 1.27+). QUIC runs over UDP — open UDP 443 in your firewall.
server {
listen 443 quic reuseport; # HTTP/3 over UDP (reuseport on ONE server only)
listen 443 ssl; # Keep TCP for HTTP/1.1 + HTTP/2
http2 on;
http3 on;
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; # QUIC always negotiates TLS 1.3
# Tell browsers HTTP/3 is available (they upgrade on the next request)
add_header Alt-Svc 'h3=":443"; ma=86400' always;
}# Verify (curl built with HTTP/3 support) curl --http3 -I https://example.com nginx -V 2>&1 | grep -o with-http_v3_module
Directive Reference
| Directive | Recommended value | Purpose |
|---|---|---|
ssl_certificate | fullchain.pem | Public cert (+ intermediates) |
ssl_certificate_key | privkey.pem | Private key |
ssl_protocols | TLSv1.2 TLSv1.3 | Allowed protocol versions |
ssl_ciphers | ECDHE/GCM suites | Cipher whitelist |
ssl_prefer_server_ciphers | off | Let clients prefer TLS 1.3 |
ssl_session_cache | shared:SSL:10m | Shared session cache across workers |
ssl_session_tickets | off | Disable for perfect forward secrecy |
http2 on | on | Enable HTTP/2 (Nginx ≥ 1.25.1) |
http3 on | on | Enable HTTP/3 with listen 443 quic (Nginx ≥ 1.25) |
Let's Encrypt with Certbot
# Install certbot and the nginx plugin sudo apt install certbot python3-certbot-nginx # Issue cert and auto-configure nginx sudo certbot --nginx -d example.com -d www.example.com # Renew all certs (run via cron or systemd timer) sudo certbot renew --dry-run sudo certbot renew # Cron entry (twice daily): # 0 0,12 * * * root certbot renew --quiet
HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;Add
preloadonly after testing — submit to hstspreload.org once stable.
add_header Inheritance Gotcha
add_header inherits from the enclosing level only if the current level has none. A single add_header inside a location silently drops every header set at server/http level — HSTS included.
server {
add_header Strict-Transport-Security "max-age=31536000" always;
location /api/ {
add_header X-Api-Version "2"; # ← HSTS is now GONE for /api/
# Fix: repeat the parent headers (or put them in a snippet and
# include it in every location that declares its own add_header)
add_header Strict-Transport-Security "max-age=31536000" always;
}
}Always use the always flag so headers are also sent on 4xx/5xx responses.
Security Headers
server_tokens off; # Server: nginx (no version) in headers + error pages add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Content-Security-Policy "default-src 'self'" always;
Diffie-Hellman Parameters
# Generate a 2048-bit DH param file (do this once) sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
ssl_dhparam /etc/nginx/dhparam.pem;
Mutual TLS (Client Certificate Auth)
server {
ssl_client_certificate /etc/ssl/ca/client-ca.crt;
ssl_verify_client on; # Require valid client cert
ssl_verify_depth 2;
location /api/ {
# $ssl_client_verify is "SUCCESS" when cert validated
if ($ssl_client_verify != "SUCCESS") { return 403; }
}
}Multiple Certs on One IP (SNI)
# Server 1 server { listen 443 ssl; server_name site1.example.com; ssl_certificate /etc/ssl/site1.crt; ssl_certificate_key /etc/ssl/site1.key; } # Server 2 — same IP, different SNI hostname server { listen 443 ssl; server_name site2.example.com; ssl_certificate /etc/ssl/site2.crt; ssl_certificate_key /etc/ssl/site2.key; }
SNI is supported by all modern clients. No extra config needed.
Useful SSL Variables
| Variable | Value |
|---|---|
$ssl_protocol | TLSv1.3 |
$ssl_cipher | Negotiated cipher suite |
$ssl_client_verify | SUCCESS, FAILED, NONE |
$ssl_client_s_dn | Client cert subject DN |
$ssl_session_reused | . if reused, r otherwise |