Skip to content
NGINX Reverse Proxy: Complete Setup Guide

NGINX Reverse Proxy: Complete Setup Guide

DodaTech Updated Jun 19, 2026 5 min read

An NGINX reverse proxy sits between clients and your backend servers, handling incoming requests and routing them to the appropriate service — adding load balancing, SSL termination, caching, and security that each individual backend doesn’t need to implement separately.

What You’ll Learn

  • What a reverse proxy is and why you need one
  • Basic NGINX reverse proxy configuration for a Node.js or Python app
  • Load balancing across multiple backend servers
  • SSL termination, caching, WebSocket proxying, security headers, and rate limiting

Why a Reverse Proxy Matters

A reverse proxy decouples your backend from the outside world. It terminates SSL (so backends don’t need certificates), balances traffic across instances, caches responses to reduce load, protects against slow clients, and adds a single point for security policies. Every production deployment should have one.

Doda Browser uses NGINX as a reverse proxy to route API traffic, serve static assets, and rate-limit public endpoints.

Learning Path

    flowchart LR
  A[Web Server Basics] --> B[Reverse Proxy<br/>You are here]
  B --> C[Load Balancing]
  B --> D[SSL Termination]
  C --> E[Production Deployment]
  D --> E
  style B fill:#f90,color:#fff
  

Basic Reverse Proxy Configuration

# /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name myapp.example.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;
    }
}

Enable and test:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Expected nginx -t output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Load Balancing

Distribute traffic across multiple backend servers:

upstream backend_servers {
    least_conn;  # Send to server with fewest connections
    server 10.0.1.1:3000 weight=3;
    server 10.0.1.2:3000 weight=2;
    server 10.0.1.3:3000 backup;  # Only used if others are down
}

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

    location / {
        proxy_pass http://backend_servers;
        proxy_next_upstream error timeout http_500;
    }
}

Load balancing methods:

  • Round-robin (default): Distributes evenly
  • least_conn: Sends to the least busy server
  • ip_hash: Same client goes to same server (session persistence)

SSL Termination

Offload SSL encryption from your backends:

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

    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 HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header X-Forwarded-Proto https;
    }
}

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

Caching

Cache responses to reduce backend load:

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

server {
    location /api/ {
        proxy_cache mycache;
        proxy_cache_valid 200 5m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating;
        add_header X-Cache-Status $upstream_cache_status;
        proxy_pass http://backend;
    }

    location /static/ {
        proxy_cache mycache;
        proxy_cache_valid 200 24h;
        proxy_pass http://static-server;
    }
}

Check cache effectiveness with the X-Cache-Status header: HIT means served from cache, MISS means fetched from backend.

WebSocket Proxying

Proxying WebSocket connections requires connection upgrade headers:

location /ws/ {
    proxy_pass http://websocket-backend:8080;
    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;  # 24 hours — WebSockets are long-lived
}

Security Headers

Add security headers to protect against common attacks:

server {
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "0" always;  # Deprecated but harmless
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'" always;
}

Rate Limiting

Protect your backends from abuse:

# Define a rate limit zone: $10 per request, burst up to 20
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

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

    location /auth/ {
        limit_req zone=api burst=5 nodelay;  # Stricter for auth
        proxy_pass http://auth-backend;
    }
}

When rate limit is exceeded, NGINX returns 503 Service Unavailable. Monitor this in your logs.

Common Errors

1. Missing X-Forwarded Headers

Backend servers see NGINX’s IP instead of the real client IP. Always forward X-Real-IP and X-Forwarded-For.

2. Incorrect SSL Certificate Path

NGINX won’t start if certificate paths are wrong. Always test with sudo nginx -t.

3. Not Setting proxy_http_version 1.1

NGINX defaults to HTTP/1.0 for proxied requests. WebSockets and keep-alive require HTTP/1.1.

4. Cache Poisoning

Improper cache configuration can serve stale or unauthorized data. Use proxy_cache_bypass and proxy_no_cache for authenticated content.

5. Timeouts on Slow Backends

Default proxy_read_timeout is 60 seconds. Increase for long-running requests.

6. Rate Limiting Too Aggressively

Setting 10r/s when your backend handles 100r/s causes unnecessary 503s. Set limits based on actual capacity.

Practice Questions

  1. What does an NGINX reverse proxy do? Sits between clients and backends, routing requests, terminating SSL, balancing load, caching, and adding security.

  2. What header must be forwarded so backends see the real client IP? X-Real-IP or X-Forwarded-For.

  3. What’s the difference between least_conn and ip_hash load balancing? least_conn sends to the least busy server. ip_hash sends the same client to the same server for session persistence.

  4. What does proxy_cache_valid 200 5m mean? Cache successful (200) responses for 5 minutes.

  5. How do you check NGINX configuration for syntax errors? Run sudo nginx -t.

Challenge: Set up an NGINX reverse proxy in front of a simple Node.js or Python app. Configure SSL with a self-signed certificate, enable caching for static files, set up rate limiting at 5 requests per second, and add all security headers. Test each feature.

FAQ

What is the difference between a reverse proxy and a forward proxy?
A forward proxy acts on behalf of clients (hiding them from servers). A reverse proxy acts on behalf of servers (hiding them from clients).
Can NGINX reverse proxy to multiple different backends?
Yes — use different location blocks to route to different backends. For example, /api/ → Node.js, /static/ → Python, /ws/ → WebSocket server.
Is NGINX the only reverse proxy option?
No — Apache, HAProxy, Caddy, Traefik, and Envoy are alternatives. NGINX is the most popular for web applications.
Does a reverse proxy add latency?
Minimal (1-5ms per request). The benefits of SSL offloading, caching, and load balancing far outweigh this small overhead.
How do I monitor NGINX reverse proxy performance?
Check access logs, error logs, the stub_status module (/nginx_status), and integrate with Prometheus/Grafana via the nginx-prometheus-exporter.

What’s Next

TutorialWhat You’ll Learn
NGINX Web Server GuideServing static files with NGINX
SSL/TLS Certificates with Let's EncryptSetting up free SSL certs for your proxy
Docker and NGINXRunning NGINX as a Docker container

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-19.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro