Nginx Complete Guide 2026: Reverse Proxy, Load Balancing, and SSL

Sanjeev SharmaSanjeev Sharma
4 min read

Advertisement

Nginx 2026: The Production Web Server

Nginx handles 34% of all web traffic. As a reverse proxy for Node.js, it adds SSL, caching, rate limiting, and load balancing without changing your application code.

Installation

sudo apt update
sudo apt install nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Reverse Proxy for Node.js

# /etc/nginx/sites-available/myapp
upstream nodejs_app {
    server 127.0.0.1:3000;
    keepalive 64;  # Persistent connections
}

server {
    listen 80;
    server_name webcoderspeed.com www.webcoderspeed.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name webcoderspeed.com www.webcoderspeed.com;

    # SSL
    ssl_certificate /etc/letsencrypt/live/webcoderspeed.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/webcoderspeed.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; 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;

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

    # Client body limit
    client_max_body_size 10M;

    # Proxy to Node.js
    location / {
        proxy_pass http://nodejs_app;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';  # WebSocket support
        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_cache_bypass $http_upgrade;

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

    # Static files — serve directly from Nginx (faster)
    location /_next/static/ {
        alias /app/.next/static/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location /public/ {
        alias /app/public/;
        expires 30d;
    }
}

Load Balancing

upstream api_servers {
    # Load balancing methods:
    # (default) round-robin
    # least_conn;    — fewest active connections
    # ip_hash;       — same client always hits same server

    least_conn;

    server 10.0.1.10:3000 weight=3;    # Gets 3x traffic
    server 10.0.1.11:3000 weight=1;
    server 10.0.1.12:3000 backup;      # Only used when others fail

    keepalive 32;

    # Health checks
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
}

server {
    listen 443 ssl http2;

    location /api/ {
        proxy_pass http://api_servers;
        health_check interval=5 fails=2 passes=2;
    }
}

Rate Limiting

http {
    # Define zones
    limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;     # API: 30/min per IP
    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;    # Login: 5/min per IP
    limit_conn_zone $binary_remote_addr zone=conn:10m;               # Max connections

    server {
        # Apply rate limiting
        location /api/ {
            limit_req zone=api burst=10 nodelay;
            limit_conn conn 10;  # Max 10 simultaneous connections per IP
            limit_req_status 429;

            proxy_pass http://nodejs_app;
        }

        location /api/auth/login {
            limit_req zone=login burst=3 nodelay;
            limit_req_status 429;

            proxy_pass http://nodejs_app;
        }

        # Custom error page for rate limited
        error_page 429 /429.json;
        location = /429.json {
            default_type application/json;
            return 429 '{"error":"Too many requests","retryAfter":60}';
        }
    }
}

Nginx as Static File Server

server {
    listen 80;
    server_name static.webcoderspeed.com;
    root /var/www/static;

    # Serve index.html for SPA
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Aggressive caching for hashed assets
    location ~* \.(js|css|png|jpg|gif|ico|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # No caching for HTML
    location ~* \.html$ {
        expires -1;
        add_header Cache-Control "no-store";
    }
}

Let's Encrypt SSL (Free)

# Install Certbot
sudo apt install certbot python3-certbot-nginx

# Get certificate (auto-configures Nginx)
sudo certbot --nginx -d webcoderspeed.com -d www.webcoderspeed.com

# Auto-renewal (already set up by certbot)
sudo certbot renew --dry-run  # Test renewal

# Cron for renewal
0 12 * * * /usr/bin/certbot renew --quiet

Nginx Performance Tuning

worker_processes auto;         # Match CPU cores
worker_connections 1024;       # Max connections per worker

events {
    use epoll;                 # Linux event model (fastest)
    multi_accept on;           # Accept multiple connections
}

http {
    sendfile on;               # Efficient file serving
    tcp_nopush on;             # Send headers in one packet
    tcp_nodelay on;            # Don't buffer small packets

    keepalive_timeout 65;
    keepalive_requests 100;

    # Buffer sizes
    client_body_buffer_size 16k;
    client_header_buffer_size 1k;
    client_max_body_size 10m;

    # Compression
    gzip on;
    gzip_comp_level 6;
    gzip_min_length 1000;
    gzip_proxied any;
    gzip_types
        text/plain
        text/css
        application/json
        application/javascript
        application/xml;

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" $request_time';

    access_log /var/log/nginx/access.log main buffer=16k;
    error_log /var/log/nginx/error.log warn;
}

Nginx + Let's Encrypt + Node.js is the classic production stack for Node.js apps. It's battle-tested, free, and handles millions of requests per second.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro