Skip to content
Codeloom
DevOps

Nginx as a Reverse Proxy

Configure Nginx as a reverse proxy — upstream servers, load balancing, SSL termination, WebSocket support, and caching.

·3 min read · By Codeloom
Intermediate 10 min read

What you'll learn

  • How a reverse proxy works and why you need one
  • Configuring Nginx to proxy to backend services
  • Load balancing across multiple servers
  • SSL termination and WebSocket proxying

Prerequisites

  • Basic Linux command line skills
  • A running backend service to proxy to

A reverse proxy sits between clients and your backend servers. Nginx is the most popular choice — it handles SSL, load balancing, caching, and static files so your application does not have to.

Basic reverse proxy

server {
    listen 80;
    server_name myapp.com;

    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;
    }
}

proxy_pass forwards requests to your backend. The proxy_set_header directives pass the original client information through.

Multiple backends with upstream

upstream api_servers {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

server {
    listen 80;
    server_name api.myapp.com;

    location / {
        proxy_pass http://api_servers;
    }
}

Load balancing methods

# Round robin (default)
upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

# Least connections
upstream backend {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

# IP hash (sticky sessions)
upstream backend {
    ip_hash;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

# Weighted
upstream backend {
    server 10.0.0.1:8080 weight=3;
    server 10.0.0.2:8080 weight=1;
}

SSL termination

Handle HTTPS at Nginx so backends receive plain HTTP.

server {
    listen 443 ssl http2;
    server_name myapp.com;

    ssl_certificate     /etc/letsencrypt/live/myapp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }
}

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

WebSocket support

location /ws {
    proxy_pass http://localhost:3000;
    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;
}

Caching

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;

server {
    location /api {
        proxy_pass http://api_servers;
        proxy_cache my_cache;
        proxy_cache_valid 200 10m;
        proxy_cache_valid 404 1m;
        add_header X-Cache-Status $upstream_cache_status;
    }
}

Rate limiting

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

server {
    location /api {
        limit_req zone=api_limit burst=20 nodelay;
        proxy_pass http://api_servers;
    }
}

Serving static files

Let Nginx serve static assets directly — far more efficient than proxying them.

server {
    location /static/ {
        alias /var/www/myapp/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    location / {
        proxy_pass http://localhost:3000;
    }
}

Health checks

upstream backend {
    server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:8080 backup;
}

If a server fails 3 times in 30 seconds, Nginx marks it as down. The backup server activates only when all primary servers are down.

Testing and reloading

nginx -t              # Test configuration
sudo nginx -s reload  # Reload without downtime

Summary

Nginx as a reverse proxy handles the concerns your application should not — SSL, load balancing, caching, rate limiting, and static file serving. Start with a basic proxy_pass, add SSL with Let’s Encrypt, and layer on caching and load balancing as your traffic grows.