Nginx Configuration Guide
Reverse proxy, SSL, load balancing, and caching. The web server that powers half the internet.
What is Nginx?
Nginx (pronounced "engine-x") sits in front of your application and handles things your app should not worry about: SSL termination, static file serving, load balancing, caching, rate limiting, and request routing. Your Node/Python/Go app just handles business logic.
Almost every production deployment uses Nginx or a similar reverse proxy. Understanding its configuration is a core DevOps skill.
Reverse Proxy with SSL
The most common setup: Nginx listens on ports 80 and 443, handles SSL, and forwards requests to your app running on a local port. This example also serves static files directly - much faster than routing them through your application.
server {
listen 80;
server_name myapp.com www.myapp.com;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name myapp.com www.myapp.com;
ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
location / {
proxy_pass http://localhost: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;
}
# Serve static files directly
location /static/ {
alias /var/www/myapp/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
}Load Balancing
Running multiple copies of your app? Nginx distributes traffic across them. If one goes down, Nginx stops sending it traffic. Round-robin is the default; use least_conn for long-running requests.
upstream backend {
# Round-robin by default
server 10.0.1.10:3000;
server 10.0.1.11:3000;
server 10.0.1.12:3000;
# Or use least connections
# least_conn;
# Or weighted distribution
# server 10.0.1.10:3000 weight=3;
# server 10.0.1.11:3000 weight=1;
}
server {
listen 443 ssl http2;
server_name myapp.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
}
}Caching and Compression
Nginx can cache responses from your backend, dramatically reducing load. The X-Cache-Status header tells you if a response came from cache (HIT) or the backend (MISS). Gzip compression reduces response sizes by 60-80%.
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m
max_size=1g inactive=60m use_temp_path=off;
server {
listen 443 ssl http2;
server_name myapp.com;
location /api/ {
proxy_pass http://localhost:3000;
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
add_header X-Cache-Status $upstream_cache_status;
}
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
}Security Hardening
Rate limiting prevents abuse. Security headers protect against common attacks. Always block access to hidden files (like .env or .git).
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
server {
listen 443 ssl http2;
server_name myapp.com;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Apply rate limiting to API
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://localhost:3000;
}
# Block common attack paths
location ~ /\. {
deny all;
}
}