architecture2024-11-25·14 min·251/348

Nginx Reverse Proxy Configuration on Linux: A Complete Guide

Setting up Nginx as a reverse proxy on Linux, including load balancing, WebSocket support, SSL termination, and performance tuning.

Introduction

Nginx is the most popular reverse proxy on Linux. It sits between clients and backend applications, handling SSL termination, load balancing, caching, and request routing. This post covers configuring Nginx as a reverse proxy for modern web applications, including WebSocket support and performance optimization.

Environment

The examples use Nginx 1.24.0 on Ubuntu 22.04 LTS, proxying requests to a Node.js application running on port 3000 and a Python API on port 8000.

nginx -v
# nginx version: nginx/1.24.0 (Ubuntu)

Problem

Your backend application serves content but should not be exposed directly to the internet. You need Nginx to:

  • Terminate SSL/TLS connections
  • Proxy requests to multiple backend services
  • Handle WebSocket connections
  • Serve static files efficiently
  • Load balance across multiple instances

Solution

1. Basic reverse proxy configuration

# /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name myapp.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
    }
}

2. SSL termination with Let's Encrypt

sudo certbot --nginx -d myapp.example.com

Or manually configure SSL:

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

    ssl_certificate /etc/letsencrypt/live/myapp.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myapp.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

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

3. Load balancing across multiple instances

upstream backend {
    least_conn;
    server 127.0.0.1:3000 weight=3;
    server 127.0.0.1:3001 weight=2;
    server 127.0.0.1:3002 backup;

    keepalive 32;
}

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

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

4. WebSocket proxy configuration

location /ws {
    proxy_pass http://127.0.0.1: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;
    proxy_send_timeout 86400;
}

5. Path-based routing

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

    location /api/ {
        proxy_pass http://127.0.0.1:8000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /admin/ {
        proxy_pass http://127.0.0.1:8001/;
        allow 192.168.1.0/24;
        deny all;
    }

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

6. Proxy buffering and caching

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_buffering on;
    proxy_buffer_size 16k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;

    proxy_cache my_cache;
    proxy_cache_valid 200 302 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
}

7. Rate limiting

http {
    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://127.0.0.1:8000;
        }
    }
}

8. Health checks

upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;

    health_check interval=10 fails=3 passes=2;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

9. Proxy timeouts

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    proxy_next_upstream error timeout http_502 http_503;
    proxy_next_upstream_tries 3;
}

10. Enable and test

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

sudo systemctl reload nginx

11. Monitor proxy performance

# Check Nginx stub_status
location /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}
curl http://localhost/nginx_status
# Active connections: 256
# server accepts handled requests
#  12345 12345 56789
# Reading: 5 Writing: 10 Waiting: 241

Lessons Learned

Nginx reverse proxy configuration is straightforward but requires attention to headers, especially X-Forwarded-For and X-Forwarded-Proto. Always use proxy_set_header Host $host to preserve the original hostname. For WebSocket connections, the Upgrade and Connection headers are essential. And always test with nginx -t before reloading to catch syntax errors.


This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.