Chapter 12 — Reverse Proxies & Load Balancing

A reverse proxy sits between the internet and your application servers. It handles SSL termination, load balancing, caching, rate limiting, and request routing — so your app doesn't have to.

Why Use a Reverse Proxy

Nginx — Full Production Configuration

# /etc/nginx/sites-available/myapp.conf
upstream app_backend {
    # Load balancing across multiple app instances
    server 127.0.0.1:3000 weight=3;
    server 127.0.0.1:3001 weight=2;
    server 127.0.0.1:3002 backup;    # Only used if others are down

    # Health checks (Nginx Plus) or use passive checks
    keepalive 32;    # Persistent connections to backend
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

# Main HTTPS server
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    # SSL Configuration
    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;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml;
    gzip_min_length 1000;

    # Static files — served directly by Nginx (fast)
    location /static/ {
        alias /var/www/myapp/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # API — proxy to backend
    location /api/ {
        proxy_pass http://app_backend;
        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 "";

        # Timeouts
        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Rate limiting
        limit_req zone=api burst=20 nodelay;
    }

    # WebSocket support
    location /ws/ {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;    # Keep WebSocket alive
    }
}

# Rate limiting zone (defined in nginx.conf http block)
# limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

Caddy — The Modern Alternative (Auto-HTTPS)

# Caddyfile — entire config for production site with auto-SSL
example.com {
    # Automatic HTTPS (Let's Encrypt) — zero configuration needed!

    # Reverse proxy to app
    reverse_proxy /api/* localhost:3000

    # Static files
    root * /var/www/myapp/static
    file_server

    # Compression
    encode gzip zstd

    # Security headers
    header {
        Strict-Transport-Security "max-age=63072000; includeSubDomains"
        X-Frame-Options DENY
        X-Content-Type-Options nosniff
    }

    # Rate limiting (with caddy-ratelimit plugin)
    rate_limit {remote.ip} 10r/s

    # Logging
    log {
        output file /var/log/caddy/access.log
        format json
    }
}
Caddy vs Nginx: Caddy automatically obtains and renews SSL certificates with zero configuration. For new projects, Caddy is often the better choice — less config, automatic HTTPS, modern defaults. Nginx is better when you need maximum performance tuning or have complex routing requirements.

Load Balancing Algorithms

AlgorithmHow It WorksBest For
Round RobinRequests distributed sequentiallyIdentical servers, stateless apps
Weighted Round RobinMore requests to higher-weight serversMixed server capacities
Least ConnectionsSend to server with fewest active connectionsVariable request durations
IP HashSame client IP always goes to same serverSession affinity (sticky sessions)
RandomRandom server selectionLarge clusters, simple

Cloud Load Balancers

TypeAWSLayerUse Case
Application LBALBLayer 7 (HTTP)Web apps, path-based routing, WebSocket
Network LBNLBLayer 4 (TCP/UDP)High performance, static IP, non-HTTP
Gateway LBGWLBLayer 3Network appliances (firewalls, IDS)