Nginx is the most popular web server and reverse proxy, powering over 30% of all websites. This guide covers the most common configurations with production-ready examples you can copy and adapt.
Want to skip the manual work? Use our free Nginx Config Generator to build a complete config visually.
1. Nginx as a Reverse Proxy
The most common setup: Nginx sits in front of your Node.js, Python, Go, or Java application, handling SSL, static files, and load balancing while proxying dynamic requests to the backend.
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;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
}
}Key headers to always include: X-Real-IP passes the client's actual IP to the backend (otherwise it sees 127.0.0.1). X-Forwarded-Proto lets the backend know the original request was HTTPS — critical for correct redirect URLs and cookie settings.
WebSocket Proxying
For WebSocket connections (Socket.IO, ws), you need additional headers:
proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 86400;
Without these, WebSocket connections fail with 400 errors. The proxy_read_timeout prevents Nginx from closing idle WebSocket connections after the default 60 seconds.
2. SSL/TLS with Let's Encrypt
The fastest way to get HTTPS working:
# Install certbot sudo apt install certbot python3-certbot-nginx # Get certificate (automatically configures Nginx) sudo certbot --nginx -d example.com -d www.example.com # Auto-renewal is set up automatically # Test it: sudo certbot renew --dry-run
For optimal SSL configuration, use only TLSv1.2 and TLSv1.3 (older versions have known vulnerabilities), enable OCSP stapling for faster certificate validation, and configure session caching to reduce TLS handshake overhead:
ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on;
3. Gzip Compression
Gzip typically reduces text-based responses by 60-80%. Essential config:
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/javascript
application/json
application/javascript
application/xml
image/svg+xml;comp_level 5 is the sweet spot — level 1-4 barely compress, level 7-9 use significantly more CPU for minimal extra compression. min_length 256 skips tiny responses where gzip overhead exceeds savings. Never compress already-compressed formats (JPEG, PNG, WOFF2, ZIP).
4. Security Headers
Five headers that cost nothing but protect against common attacks:
# Prevent clickjacking add_header X-Frame-Options "SAMEORIGIN" always; # Prevent MIME-type sniffing add_header X-Content-Type-Options "nosniff" always; # Force HTTPS for 2 years (enable after confirming HTTPS works) add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # Control referrer information add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Restrict browser features add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
5. Load Balancing
Distribute traffic across multiple backend servers:
upstream backend {
least_conn; # or: ip_hash for sticky sessions
server 10.0.0.1:8080 weight=3;
server 10.0.0.2:8080 weight=1;
server 10.0.0.3:8080 backup;
}
server {
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503;
}
}Three methods: Round Robin (default) distributes equally. Least Connections sends traffic to the least busy server — better when request times vary. IP Hash ensures the same client always hits the same backend — useful for apps that store sessions in memory.
6. Static File Caching
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|woff2|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}This tells browsers to cache static assets for 30 days. immutable means the browser won't even send a conditional request to check if the file changed — it trusts the cache completely. Disable access logs for static files to reduce I/O.
7. Rate Limiting
# Define zone (in http block)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# Apply to location
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
}rate=10r/s allows 10 requests per second per IP. burst=20 allows short bursts of up to 20 extra requests. nodelay processes burst requests immediately instead of queuing them.
Generate Your Config
Instead of assembling these blocks manually, use our free Nginx Config Generator. Select your server type (reverse proxy, static, PHP, load balancer), configure SSL, security headers, and gzip — get a complete production-ready nginx.conf in seconds.