Overview
Nginx config is organized as a tree of contexts: http contains server blocks, which contain location blocks. Directives inherit from the enclosing context unless overridden. This card covers the directives that make up 90% of real configs: reverse proxy, static file serving, SSL termination, redirects, and compression. Test config with nginx -t before reloading; reload with nginx -s reload (zero downtime) rather than a full restart.
Block structure
The three main blocks and their purpose.
| Block | Scope | Typical content |
|---|---|---|
http {} | Global HTTP settings | include mime.types, logging format, gzip, upstream definitions. |
server {} | One virtual host | listen, server_name, SSL config, root, location blocks. |
location {} | URL prefix or regex | proxy_pass, try_files, return, caching, headers. |
upstream {} | Backend pool | server directives listing backends; keepalive. |
http {
include mime.types;
default_type application/octet-stream;
upstream api_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name example.com;
location /api/ {
proxy_pass http://api_backend/;
}
location / {
root /var/www/html;
try_files $uri $uri/ /index.html;
}
}
}Location matching
Nginx tests location blocks in a specific order; the first match wins except for exact matches.
| Modifier | Syntax | Priority | Notes |
|---|---|---|---|
| Exact | location = /path | Highest | Must match the URI exactly; no trailing slash allowed. |
| Preferential prefix | location ^~ /path | Second | Stops regex search once matched. |
| Case-sensitive regex | location ~ \.php$ | Third | First matching regex wins. |
| Case-insensitive regex | location ~* \.jpg$ | Third | Same priority as ~; order in file breaks ties. |
| Prefix | location /path | Lowest | Longest prefix match is used if no regex matches. |
location = /favicon.ico { access_log off; return 204; }
location ^~ /static/ { root /var/www; expires 1y; }
location ~* \.(jpg|png)$ { expires 30d; add_header Cache-Control "public"; }
location / { try_files $uri $uri/ =404; }Common directives
The directives to know for proxying, serving files, and redirects.
| Directive | Context | What it does |
|---|---|---|
proxy_pass | location | Forward requests to an upstream URL or pool. |
proxy_set_header | location | Set or forward a header to the upstream. |
try_files | location | Test file paths in order; use last item as fallback or error. |
return | server, location | Immediately return a status or redirect. |
rewrite | server, location | Rewrite the URI and optionally redirect. |
root | server, location | Document root; path is prepended to the URI. |
alias | location | Replace the location prefix with the given path. |
index | server, location | Default file to serve for directory requests. |
expires | location | Set Cache-Control and Expires headers. |
add_header | server, location | Add a response header. |
# Reverse proxy with forwarded headers
location /api/ {
proxy_pass http://api_backend/;
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_http_version 1.1;
proxy_set_header Connection ""; # enable keepalive to upstream
}
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# Permanent redirect
location /old-path {
return 301 /new-path;
}SSL configuration
Minimal TLS 1.2/1.3 config for a modern 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_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}Generate certificates with Certbot: certbot --nginx -d example.com. Certbot edits the nginx config and sets up auto-renewal.
Compression and performance
Enable gzip for text responses; skip binary formats.
http {
gzip on;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml image/svg+xml;
gzip_min_length 1024;
gzip_comp_level 6; # 1 (fast) to 9 (best compression); 6 is a good balance
gzip_vary on;
gzip_proxied any;
# Serve pre-compressed .gz files if they exist
gzip_static on;
# Buffer tuning for proxied responses
proxy_buffers 16 4k;
proxy_buffer_size 4k;
keepalive_timeout 65;
sendfile on;
tcp_nopush on;
}Common gotchas
proxy_pass http://backend/(with trailing slash) strips the matched location prefix.proxy_pass http://backend(no trailing slash) passes the full URI. This mismatch causes 404s; pick the form that matches your intent.add_headerin a child block replaces, rather than adds to, headers set in a parent block. Set all security headers in one place to avoid this.rootprepends the root to the full URI.aliasreplaces the location prefix. Confusing the two produces 404s. Usealiasfor locations whose path on disk differs from the URI prefix.try_fileswith=404as a fallback returns 404 directly. Using@fallback(a named location) allows custom 404 handling.nginx -ttests syntax but not runtime behavior (e.g., unreachable upstreams). Test proxy connectivity withcurlfrom the nginx host.- The regex location
~* \.(gif|jpg)$must come before broader prefix locations in the file if Nginx must prefer it; the file order matters for same-priority matches.