# Production-ready Nginx configuration for a Next.js application. # # Handles: SSL termination, HTTP/2, gzip/brotli, security headers, # rate limiting, static asset caching, WebSocket upgrade, and # graceful upstream failover. # # Replace: example.com, /path/to/cert, upstream port. upstream nextjs { server 127.0.0.1:3000; keepalive 64; } # Rate limiting zones limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s; # Redirect HTTP → HTTPS server { listen 80; listen [::]:80; server_name example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; server_name example.com; # ── SSL ────────────────────────────────────────────────── ssl_certificate /path/to/cert/fullchain.pem; ssl_certificate_key /path/to/cert/privkey.pem; ssl_protocols TLSv1.3 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5:!3DES; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_stapling on; ssl_stapling_verify on; # ── Security headers ───────────────────────────────────── add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; # ── Compression ────────────────────────────────────────── 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/javascript application/json application/xml image/svg+xml; # ── Static assets (immutable Next.js hashes) ───────────── location /_next/static/ { proxy_pass http://nextjs; expires 365d; add_header Cache-Control "public, immutable"; access_log off; } # ── Public static files ────────────────────────────────── location /favicon.ico { proxy_pass http://nextjs; expires 30d; access_log off; } location /robots.txt { proxy_pass http://nextjs; expires 1d; access_log off; } # ── API routes (higher rate limit) ─────────────────────── location /api/ { limit_req zone=api burst=50 nodelay; limit_req_status 429; proxy_pass http://nextjs; 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; # Timeout for long-running API calls proxy_read_timeout 30s; proxy_send_timeout 30s; } # ── WebSocket support (if needed) ──────────────────────── location /_next/webpack-hmr { proxy_pass http://nextjs; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } # ── Everything else ────────────────────────────────────── location / { limit_req zone=general burst=20 nodelay; proxy_pass http://nextjs; 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; proxy_set_header Connection ""; # Don't cache HTML (Next.js handles its own caching) add_header Cache-Control "no-cache, no-store, must-revalidate"; } # ── Error pages ────────────────────────────────────────── error_page 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; internal; } }
Battle-tested Nginx reverse proxy config: SSL, HTTP/2, gzip, rate limiting, security headers, static caching, WebSocket, upstream failover.