Nginx

nginx quick reference cheat sheet

#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

Directive Meaning
(default) round‑robin
least_conn least connections
ip_hash sticky by client IP
hash key hash by custom key

#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

Variable Description
$host Host header / server name
$server_name Chosen server_name
$remote_addr Client IP
$http_user_agent User‑Agent
$request_method GET/POST/...

#Paths & Files

Variable Description
$document_root Current root
$realpath_root Symlink‑resolved root
$request_uri Path + query
$uri Normalized URI
$args Raw query string

#Upstream

Variable Description
$upstream_addr Upstream server(s)
$upstream_status Upstream status
$upstream_response_time Time from upstream

#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;