📚 Learning Objectives
By the end of this module, you will: - Understand what a reverse proxy is - Configure NGINX as a reverse proxy - Proxy to different backend servers - Handle headers and proxy settings - Configure WebSocket proxying - Understand proxy buffering
🔄 What is a Reverse Proxy?
A reverse proxy is a server that sits between clients and backend servers. It receives requests from clients and forwards them to backend servers, then returns the response.
Forward Proxy vs Reverse Proxy
Forward Proxy: - Client → Proxy → Internet - Hides client identity - Used by clients (browsers)
Reverse Proxy: - Client → Proxy → Backend Server - Hides backend server identity - Used by servers
Benefits of Reverse Proxy
- Load Distribution: Distribute requests across multiple backends
- SSL Termination: Handle HTTPS, forward HTTP to backends
- Caching: Cache responses from backends
- Security: Hide backend server details
- Compression: Compress responses
- Routing: Route requests based on URL patterns
🔧 Basic Reverse Proxy Configuration
Simple Proxy
server {
listen 80;
server_name example.com;
location / {
# Proxy all requests to backend server
proxy_pass http://localhost:8080;
# Pass original host header
proxy_set_header Host $host;
# Pass client IP
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 to Different Backend
server {
listen 80;
server_name example.com;
# Proxy to backend application server
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;
}
}
📋 Essential Proxy Directives
proxy_pass
Forwards requests to a backend server:
location / {
proxy_pass http://backend-server:port;
}
Important Notes:
- If proxy_pass has a path, the location path is replaced
- If proxy_pass has no path, the full URI is passed
# Example 1: Full URI passed
location /app/ {
proxy_pass http://backend:8080; # /app/users → http://backend:8080/app/users
}
# Example 2: Path replaced
location /app/ {
proxy_pass http://backend:8080/api/; # /app/users → http://backend:8080/api/users
}
proxy_set_header
Sets headers sent to the backend:
location / {
proxy_pass http://backend:8080;
# Standard headers
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;
# Custom headers
proxy_set_header X-Custom-Header "value";
}
Common Headers Explained
- Host: Original host from client request
- X-Real-IP: Client's real IP address
- X-Forwarded-For: Chain of proxy IPs
- X-Forwarded-Proto: Original protocol (http/https)
- X-Forwarded-Host: Original host header
🎯 Complete Reverse Proxy Configuration
server {
listen 80;
server_name example.com;
# Increase timeouts for backend processing
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
location / {
proxy_pass http://127.0.0.1:3000;
# Headers
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 X-Forwarded-Host $host;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
# Don't pass proxy headers to backend
proxy_set_header Connection "";
# Enable keepalive to backend
proxy_http_version 1.1;
}
}
🔌 WebSocket Proxying
WebSocket connections require special handling:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend:8080;
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;
}
# WebSocket location
location /ws/ {
proxy_pass http://backend:8080;
# WebSocket headers
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;
# WebSocket timeouts
proxy_read_timeout 86400;
}
}
🎨 Routing Based on URL Patterns
Different Backends for Different Paths
server {
listen 80;
server_name example.com;
# API requests to API server
location /api/ {
proxy_pass http://api-server:8080;
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;
}
# Admin panel to admin server
location /admin/ {
proxy_pass http://admin-server:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# All other requests to main app
location / {
proxy_pass http://app-server: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 Buffering
Buffering controls how NGINX handles responses from backend:
location / {
proxy_pass http://backend:8080;
# Enable buffering
proxy_buffering on;
# Buffer sizes
proxy_buffer_size 4k; # First part of response
proxy_buffers 8 4k; # Number and size of buffers
proxy_busy_buffers_size 8k; # Buffers that can be busy
proxy_max_temp_file_size 2048m; # Max temp file size
proxy_temp_file_write_size 32k; # Write size to temp file
# Disable buffering for streaming/SSE
# proxy_buffering off;
}
When to disable buffering: - Server-Sent Events (SSE) - Real-time streaming - Long polling - WebSockets (handled differently)
🔒 HTTPS to HTTP Proxying (SSL Termination)
Terminate SSL at NGINX and forward HTTP to backend:
# HTTPS frontend
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
# Proxy to HTTP backend
proxy_pass http://127.0.0.1:8080;
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 https; # Important!
}
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
🌐 Proxy to External Servers
server {
listen 80;
server_name example.com;
location / {
# Proxy to external server
proxy_pass http://external-server.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
🔍 Error Handling
Custom Error Pages
location / {
proxy_pass http://backend:8080;
# Handle backend errors
proxy_intercept_errors on;
error_page 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Backend Health Checks
location / {
proxy_pass http://backend:8080;
# Don't proxy if backend is down
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
📝 Practical Examples
Example 1: Node.js Application
server {
listen 80;
server_name myapp.com;
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;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
Example 2: Python Django Application
server {
listen 80;
server_name myapp.com;
location / {
proxy_pass http://127.0.0.1:8000;
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;
# Required for Django
proxy_redirect off;
}
# Serve static files directly
location /static/ {
alias /path/to/static/files/;
expires 30d;
}
location /media/ {
alias /path/to/media/files/;
expires 30d;
}
}
Example 3: Multiple Services
server {
listen 80;
server_name example.com;
# Main application
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;
}
# API service
location /api/ {
proxy_pass http://127.0.0.1:3001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# WebSocket service
location /ws/ {
proxy_pass http://127.0.0.1:3002;
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;
}
# Static files served directly
location /static/ {
root /var/www/static;
expires 1y;
}
}
🧪 Testing Reverse Proxy
Test Proxy Configuration
# Test configuration
sudo nginx -t
# Reload NGINX
sudo systemctl reload nginx
# Test from command line
curl -I http://example.com
# Test with specific headers
curl -H "Host: example.com" http://your-server-ip
Debug Proxy Headers
Add temporary headers to see what's being sent:
location / {
proxy_pass http://backend:8080;
# Debug headers (remove in production)
add_header X-Debug-Backend "backend:8080" always;
add_header X-Debug-Host $host always;
}
📝 Exercises
- Set up a basic reverse proxy to a backend application
- Configure proxy headers (X-Real-IP, X-Forwarded-For, etc.)
- Set up routing: different URL paths to different backends
- Configure WebSocket proxying
- Set up SSL termination (HTTPS frontend, HTTP backend)
- Configure proxy buffering settings
- Test error handling when backend is down
- Combine static file serving with reverse proxy
✅ Checklist
- [ ] Understand what a reverse proxy is
- [ ] Can configure basic reverse proxy
- [ ] Understand proxy_pass directive
- [ ] Can set proper proxy headers
- [ ] Can configure WebSocket proxying
- [ ] Understand proxy buffering
- [ ] Can route requests based on URL patterns
- [ ] Can handle SSL termination
➡️ Next Steps
Proceed to Module 3: Load Balancing to learn how to distribute traffic across multiple backend servers.