Click any item to expand the explanation and examples.
🔧 Commands
nginx CLI cli
# Test config (always do this before reload!) nginx -tReload config (no downtime)
sudo nginx -s reload
Start / stop
sudo systemctl start nginx sudo systemctl stop nginx sudo systemctl restart nginx
Check status
sudo systemctl status nginx
View error log
tail -f /var/log/nginx/error.log
View access log
tail -f /var/log/nginx/access.log
Config file locations
/etc/nginx/nginx.conf (main config)
/etc/nginx/sites-available/ (site configs)
/etc/nginx/sites-enabled/ (symlinks to active sites)
/etc/nginx/conf.d/ (additional configs)
🌐 Server Blocks
Basic static site config
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Reverse proxy (Node, Python, etc.) config
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
}
See also: 502 Bad Gateway fix for when the proxy can’t reach your app.
🔒 SSL / HTTPS
SSL with Let's Encrypt ssl
# Install certbot sudo apt install certbot python3-certbot-nginxGet certificate (auto-configures Nginx)
sudo certbot —nginx -d example.com -d www.example.com
Auto-renewal (usually set up automatically)
sudo certbot renew —dry-run
Manual SSL config
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; # Modern SSL settings ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; # HSTS add_header Strict-Transport-Security "max-age=63072000" always;}
HTTP → HTTPS redirect ssl
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
🔀 Redirects & Rewrites
Redirects redirect
# Permanent redirect (301)
location /old-page {
return 301 /new-page;
}
www to non-www
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
Redirect all to one page
location / {
return 302 /maintenance.html;
}
Rewrite (internal, URL stays the same in browser)
rewrite ^/blog/(.*)$ /articles/$1 last;
Trailing slash
rewrite ^([^.]*[^/])$ $1/ permanent;
📁 Location Blocks
Location matching rules location
# Prefix match (default)
location /images/ {
root /var/www;
}
Exact match (highest priority)
location = /favicon.ico {
log_not_found off;
access_log off;
}
Regex match (case sensitive)
location ~ .php$ {
# handle PHP
}
Regex match (case insensitive)
location ~* .(jpg|jpeg|png|gif|ico)$ {
expires 30d;
add_header Cache-Control “public, immutable”;
}
Priority order:
1. = (exact)
2. ^~ (prefix, no regex check)
3. ~ or ~* (regex)
4. prefix (longest match)
⚡ Performance
Caching and compression perf
# Gzip compression gzip on; gzip_types text/plain text/css application/json application/javascript text/xml; gzip_min_length 1000;Static file caching
location ~* .(css|js|jpg|jpeg|png|gif|ico|svg|woff2)$ { expires 1y; add_header Cache-Control “public, immutable”; access_log off; }
Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ { limit_req zone=api burst=20 nodelay; proxy_pass http://localhost:3000; }
File upload size
client_max_body_size 50M;
🔍 Common Patterns
SPA (React, Vue, Angular) pattern
server {
listen 80;
server_name app.example.com;
root /var/www/app/dist;
index index.html;
# All routes → index.html (client-side routing)
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
CORS headers pattern
location /api/ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type";
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://localhost:3000;
}
See also: CORS error fix