Nginx basics cheatsheet.

Install

# Debian/Ubuntu
apt install nginx

# Alpine
apk add nginx

# Docker
docker run -d -p 80:80 nginx

CLI

nginx                          # start
nginx -t                       # test config
nginx -T                       # test + print full config
nginx -s reload                # reload
nginx -s stop                  # stop gracefully (quit)
nginx -s quit                  # graceful (waits for connections)
nginx -s reopen                # reopen log files
nginx -v / nginx -V            # version, build info

Config structure

/etc/nginx/
├── nginx.conf              # main
├── conf.d/                 # included http blocks
│   └── *.conf
├── sites-available/        # Debian: define site configs
├── sites-enabled/          # Debian: enabled (symlinked)
├── mime.types
└── ssl/

nginx.conf skeleton

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;
    
    gzip on;
    
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Server block (virtual host)

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    
    root /var/www/example;
    index index.html;
    
    location / {
        try_files $uri $uri/ =404;
    }
}

Listen variations

listen 80;                          # IPv4 all
listen 80 default_server;           # default for unmatched
listen [::]:80;                     # IPv6 all
listen 443 ssl http2;
listen 127.0.0.1:8080;
listen unix:/run/nginx.sock;        # unix socket

Locations

location / { ... }                  # everything (lowest priority)
location = /exact { ... }           # exact match (highest)
location /prefix { ... }            # prefix match
location ^~ /prefix { ... }         # prefix, skip regex
location ~ \.php$ { ... }           # case-sensitive regex
location ~* \.(jpg|png)$ { ... }    # case-insensitive regex

Match order:

  1. = /exact
  2. ^~ /prefix (longest)
  3. ~ regex (first match)
  4. Prefix (longest)

try_files

location / {
    try_files $uri $uri/ /index.html;     # SPA fallback
    try_files $uri @backend;              # fall through to upstream
}

location @backend {
    proxy_pass http://app;
}

return / rewrite

return 301 https://$host$request_uri;
return 404 "Not Found";
return 200 "OK" application/json;

rewrite ^/old(.*)$ /new$1 permanent;
rewrite ^/api/(.*)$ /v1/$1 last;

Static file serving

server {
    listen 80;
    server_name static.example.com;
    
    root /var/www/static;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    location ~* \.(jpg|jpeg|png|gif|webp|css|js|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

Auto-index (directory listing)

location /downloads/ {
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;
}

Custom error pages

error_page 404 /404.html;
error_page 500 502 503 504 /5xx.html;

location = /404.html { internal; root /var/www/errors; }
location = /5xx.html { internal; root /var/www/errors; }

Reload safely

nginx -t && systemctl reload nginx

Reload is graceful: spawns new workers, drains old ones.

Logs

tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

Per-server logs:

server {
    access_log /var/log/nginx/example.com.access.log main;
    error_log /var/log/nginx/example.com.error.log warn;
}

Disable access log:

location = /health {
    access_log off;
    return 200 "ok";
}

Includes

include /etc/nginx/snippets/ssl.conf;
include /etc/nginx/conf.d/*.conf;

Reuse snippets.

Variables

$host                # Host header
$request_uri         # /foo?bar=1
$uri                 # /foo
$remote_addr         # client IP
$scheme              # http or https
$args                # query string
$arg_NAME            # specific query arg
$cookie_NAME         # cookie
$http_HEADER         # request header (e.g. $http_user_agent)
$server_name
$request_method
$request_id

conf reload test workflow

nginx -t                            # validate
nginx -s reload                     # apply
systemctl reload nginx              # alternative

If invalid: old config stays. No downtime.

Common mistakes

  • Forgetting nginx -t before reload.
  • Wrong server_name order — default_server matters.
  • Mixing try_files and proxy_pass in same location wrong.
  • Missing ; at end of directive.
  • return after rewrite — both not reached.

Read this next

If you want my Nginx config templates, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .