Nginx CHEAT SHEET

nginx quick reference cheat sheet

16
Sections
48
Cards

#Getting Started

Install & Service
  • Ubuntu/Debian
    $ sudo apt update && sudo apt install -y nginx
    
  • RHEL/CentOS
    $ sudo yum install -y epel-release nginx && sudo systemctl enable --now nginx
    
  • Service
    $ sudo systemctl status nginx
    $ sudo systemctl reload nginx
    $ sudo systemctl restart nginx
    $ sudo nginx -t   # test config
    $ nginx -V        # built modules
    
Key Paths
  • /etc/nginx/nginx.conf (main config)
  • /etc/nginx/conf.d/*.conf (drop‑ins)
  • /etc/nginx/sites-available/ + sites-enabled/ (Debian style)
  • /var/www/html (default docroot)
  • logs: /var/log/nginx/access.log, /var/log/nginx/error.log
Minimal HTTP Server
# /etc/nginx/conf.d/example.conf
server {
  listen 80;
  server_name example.com;
  root /var/www/example/public;

  location / {
    try_files $uri $uri/ =404;
  }
}

#Config Structure

Core Blocks
  • main (global)
  • events (worker connections)
  • httpserverlocation
  • stream (TCP/UDP)
  • upstream (load balancers)
user  www-data;
worker_processes auto;

events { worker_connections 1024; }

http {
  include       mime.types;
  default_type  application/octet-stream;
  sendfile      on;
  keepalive_timeout 65;

  # servers / includes go here...
}
Context & Order
  • location match order:
    1. Exact =
    2. ^~ (no regex if matched)
    3. Regex ~ / ~* (first match)
    4. Prefix (longest path)
  • try_files evaluates in order then falls back.
location = /healthz { return 204; }
location ^~ /static/ { expires 7d; }
location ~* \.(png|jpg|css|js)$ { expires 7d; }
location / { try_files $uri $uri/ /index.html; }
Common Includes
http {
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/snippets/*.conf; # Ubuntu/Debian
}

#Virtual Hosts & Redirects

Basic Server Block
server {
  listen 80;
  server_name example.com www.example.com;
  root /var/www/example/public;
  index index.html index.htm;
}
Redirect HTTP→HTTPS
server {
  listen 80;
  server_name example.com www.example.com;
  return 301 https://example.com$request_uri;
}
Canonical Host
# Force non-www
server {
  listen 80;
  server_name www.example.com;
  return 301 $scheme://example.com$request_uri;
}

#TLS/SSL

Basic TLS Server
server {
  listen 443 ssl http2;
  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_prefer_server_ciphers off;

  root /var/www/example/public;
}
HSTS & Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
Let’s Encrypt (Certbot)
$ sudo apt install -y certbot python3-certbot-nginx
$ sudo certbot --nginx -d example.com -d www.example.com
$ sudo systemctl list-timers | grep certbot   # auto-renew

#Reverse Proxy

Basic Proxy
upstream app {
  server 127.0.0.1:3000;
  # server unix:/run/app.sock; # alternative
}

server {
  listen 80;
  server_name api.example.com;

  location / {
    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_pass http://app;
  }
}
WebSockets / HTTP Upgrade
location /socket.io/ {
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_pass http://app;
}
Timeouts & Buffers
proxy_connect_timeout 5s;
proxy_send_timeout    60s;
proxy_read_timeout    60s;
proxy_buffering       on;
proxy_buffers 16 16k;
proxy_busy_buffers_size 24k;

#Static, Compression, Caching

Static Files
location /assets/ {
  alias /var/www/example/assets/;
  access_log off;
  expires 7d;
  add_header Cache-Control "public, max-age=604800, immutable";
}
Gzip
gzip on;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
gzip_comp_level 5;
(Optional) Brotli (if compiled)
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/javascript application/json image/svg+xml;

#Caching & Microcaching

Proxy Cache Zone
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=micro:10m max_size=1g inactive=10m use_temp_path=off;
map $request_method $no_cache { default 0; POST 1; PUT 1; PATCH 1; DELETE 1; }
Use the Cache
location /api/ {
  proxy_cache micro;
  proxy_cache_bypass $no_cache;
  proxy_no_cache $no_cache;
  proxy_cache_valid 200 301 302 10s;
  proxy_cache_valid any 1s;
  add_header X-Cache-Status $upstream_cache_status;
  proxy_pass http://app;
}
Conditional Bypass
# Skip cache when logged in (example cookie)
map $http_cookie $logged_in {
  default 0;
  ~*"(session|auth|logged_in)" 1;
}
proxy_cache_bypass $logged_in;
proxy_no_cache $logged_in;

#PHP‑FPM / FastCGI

Basic PHP Handler
location ~ \.php$ {
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
  fastcgi_param DOCUMENT_ROOT $realpath_root;
  fastcgi_pass unix:/run/php/php8.2-fpm.sock;
  fastcgi_buffers 16 16k;
  fastcgi_read_timeout 60s;
}
try_files Front Controller
location / {
  try_files $uri $uri/ /index.php?$args;
}
Security Tips
location ~* \.(?:ini|log|sh|sql|bak)$ { deny all; }
location ~ /\.(?!well-known) { deny all; }

#Rewrites & Routing

try_files
location / {
  try_files $uri $uri/ /index.html;
}
Regex Rewrites
# Remove trailing slash (except root)
if ($request_uri ~* "^(.+)/+$") { return 301 $1; }

# Legacy path to new path
rewrite ^/old/(.*)$ /new/$1 permanent;
SPA / History API
location / {
  try_files $uri /index.html;
}

#Rate Limiting & DoS Mitigation

Request Rate
# 10 req/s with burst 20 per IP
limit_req_zone $binary_remote_addr zone=reqs:10m rate=10r/s;

server {
  location /api/ {
    limit_req zone=reqs burst=20 nodelay;
  }
}
Concurrent Connections
limit_conn_zone $binary_remote_addr zone=conns:10m;
server {
  location /download/ {
    limit_conn conns 10;
  }
}
Body Size & Timeouts
client_max_body_size 25m;
client_body_timeout 30s;
keepalive_timeout 65s;

#Security Headers & Access

Basic Hardening
server_tokens off;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
Allow/Deny
location /admin/ {
  allow 192.168.0.0/16;
  deny all;
}
CORS (Example)
location /api/ {
  add_header Access-Control-Allow-Origin "https://app.example.com" always;
  add_header Access-Control-Allow-Credentials "true" always;
  if ($request_method = OPTIONS) {
    add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
    add_header Access-Control-Allow-Headers "Authorization, Content-Type";
    return 204;
  }
  proxy_pass http://app;
}

#Logging & Debug

Formats
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" '
                '$request_time $upstream_response_time';

access_log /var/log/nginx/access.log main;
error_log  /var/log/nginx/error.log warn;
Per‑Location Logging
location /healthz { access_log off; }
Debugging
$ sudo nginx -t
$ sudo nginx -s reload
$ tail -f /var/log/nginx/error.log

#Upstreams & LB

Strategies
DirectiveMeaning
(default)round‑robin
least_connleast connections
ip_hashsticky by client IP
hash keyhash by custom key

{.show-header}

Example Upstream
upstream api_backends {
  least_conn;
  server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
  server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
  # server backup.example:8080 backup;
}
Health & Failover
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;

#Useful Variables

Request & Client
VariableDescription
$hostHost header / server name
$server_nameChosen server_name
$remote_addrClient IP
$http_user_agentUser‑Agent
$request_methodGET/POST/...

{.show-header .bold-first}

Paths & Files
VariableDescription
$document_rootCurrent root
$realpath_rootSymlink‑resolved root
$request_uriPath + query
$uriNormalized URI
$argsRaw query string

{.show-header .bold-first}

Upstream
VariableDescription
$upstream_addrUpstream server(s)
$upstream_statusUpstream status
$upstream_response_timeTime from upstream

{.show-header .bold-first}

#Stream (TCP/UDP)

TCP Proxy
stream {
  upstream db {
    server 10.0.0.10:5432;
    server 10.0.0.11:5432;
  }
  server {
    listen 5432;
    proxy_pass db;
  }
}
UDP Proxy
stream {
  server {
    listen 53 udp;
    proxy_responses 1;
    proxy_timeout 2s;
    proxy_pass 1.1.1.1:53;
  }
}
Access Control
stream {
  server {
    listen 6379;
    allow 10.0.0.0/8;
    deny all;
    proxy_pass 127.0.0.1:6379;
  }
}

#Snippets

Security Snippet
# /etc/nginx/snippets/security.conf
server_tokens off;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
PHP Snippet
# /etc/nginx/snippets/fastcgi-php.conf
location ~ \.php$ {
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
  fastcgi_param DOCUMENT_ROOT $realpath_root;
  fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
Proxy Headers Snippet
# /etc/nginx/snippets/proxy-headers.conf
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;