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
| Error | Where to look |
|---|---|
| 502 Bad Gateway | the app does not listen on the proxy_pass address: ss -ltnp | grep 3000 |
| 504 Gateway Timeout | the app answers slowly: proxy_read_timeout 120s; |
| 413 | client_max_body_size |
| redirect loop | the app behind the proxy does not see HTTPS: X-Forwarded-Proto |
