Skip to content
RunStack
Wiki· Updated 19 Oct 2026

Cheat sheet: nginx

nginx: config test, reload, reverse proxy, HTTPS, redirects, limits, logs.

Control

nginx -t                                   # test the config
nginx -t && systemctl reload nginx         # apply without downtime
nginx -T | less                            # the whole effective config
systemctl status nginx
tail -f /var/log/nginx/access.log /var/log/nginx/error.log
ls /etc/nginx/sites-enabled/ /etc/nginx/conf.d/
ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app

Reverse proxy

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

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

WebSocket (chats, dashboards, Uptime Kuma) — add to the location:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;

HTTPS (Let's Encrypt)

apt install certbot python3-certbot-nginx
certbot --nginx -d app.example.com -d www.app.example.com
certbot renew --dry-run                    # test renewal
certbot certificates                       # what is issued and until when
server {
    listen 443 ssl;
    http2 on;
    server_name app.example.com;
    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
    add_header Strict-Transport-Security "max-age=31536000" always;
}

Redirects

# http → https
server { listen 80; server_name app.example.com; return 301 https://$host$request_uri; }

# www → apex
server { listen 443 ssl; server_name www.example.com; return 301 https://example.com$request_uri; }

# old path → new
location = /old { return 301 /new; }

Static files and SPA

root /var/www/app;
location / { try_files $uri $uri/ /index.html; }
location ~* \.(js|css|png|jpg|svg|woff2)$ { expires 30d; access_log off; }

Limits and access

client_max_body_size 100m;                 # upload size (413 Request Entity Too Large)

# in http {}
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# in location
limit_req zone=login burst=5 nodelay;

# your IPs only
allow 203.0.113.10;
allow 10.0.0.0/8;
deny all;

# password
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;

A password file without apache2-utils — the htpasswd generator.

Common errors

ErrorWhere to look
502 Bad Gatewaythe app does not listen on the proxy_pass address: ss -ltnp | grep 3000
504 Gateway Timeoutthe app answers slowly: proxy_read_timeout 120s;
413client_max_body_size
redirect loopthe app behind the proxy does not see HTTPS: X-Forwarded-Proto