A reverse proxy sits in front of your backend servers, accepting client requests and forwarding them on. It's different from a forward proxy which sits in front of clients. Reverse proxies are essential for scalable, secure infrastructure.
Forward Proxy vs Reverse Proxy
Forward Proxy (client side):
Client → Forward Proxy → Internet → External Server
(Client wants to go OUT)
Example: Corporate proxy, VPN
Reverse Proxy (server side):
Internet/Client → Reverse Proxy → Backend Servers
(Server wants to RECEIVE protected)
Example: nginx, HAProxy in front of web servers
What is a Reverse Proxy?
Reverse Proxy = Server that forwards requests to backend servers:
Client makes request to: api.example.com
↓
Reverse Proxy receives it
├─ Checks cache (maybe has cached response)
├─ Routes to backend server (round-robin, least-conn, etc)
├─ Waits for response
└─ Returns to client
Client never directly connects to backend
Why Use a Reverse Proxy?
1. Security
- Backend servers hidden from internet
- Firewall only needs to allow reverse proxy
- Black-box defense against attacks
2. Load Balancing
- Distribute requests across multiple backends
- Add/remove servers without client awareness
- Health checks automatically remove failures
3. Caching
- Cache responses from backend
- Reduce backend load
- Faster responses for frequently accessed content
4. SSL/TLS Termination
- Encryption work done at proxy
- Backend servers don't need certificates
- Simpler backend code
5. Rewrite/Transform
- Modify requests/responses
- Add security headers
- Redirect based on URLs
6. Compression
- Compress responses to clients
- Save bandwidth
- Invisible to clients
Reverse Proxy vs Load Balancer
| Aspect | Reverse Proxy | Load Balancer |
|---|---|---|
| Purpose | Request routing, caching, security | Distribute traffic equally |
| Placement | Single entry point | Between clients and servers |
| Routing | Content-based, URL-based | Round-robin, least-conn |
| Caching | Often enabled | Not typical |
| SSL | Often terminates here | Can terminate here too |
| Use Case | API gateway, web server | High-traffic services |
Often combined: Reverse proxy that also does load balancing!
nginx as Reverse Proxy
Basic Configuration:
server {
listen 80;
server_name api.example.com;
location / {
# Forward all requests to backend
proxy_pass http://backend-server:8080;
# Forward 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_set_header Host $host;
}
}With Backend Pool:
upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
}
}With Caching:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m;
server {
listen 80;
location / {
# Enable caching
proxy_cache my_cache;
proxy_cache_valid 200 1h; # Cache 200 responses for 1 hour
proxy_cache_valid 404 10m; # Cache 404s for 10 minutes
proxy_cache_use_stale error timeout; # Use stale if backend down
# Add header showing cache status
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend;
}
}Content-Based Routing
Route based on URL path:
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://api-backend:8080;
}
location /static/ {
proxy_pass http://static-backend:8080;
}
location /uploads/ {
proxy_pass http://file-backend:9000;
}
}Route based on hostname:
server {
server_name api.example.com;
proxy_pass http://api-backend:8080;
}
server {
server_name www.example.com;
proxy_pass http://web-backend:8080;
}
server {
server_name static.example.com;
proxy_pass http://static-backend:8080;
}Request modification
Rewrite URLs:
server {
listen 80;
# Rewrite /v1/users to /users on backend
location /v1/ {
rewrite ^/v1/(.*) /$1 break;
proxy_pass http://backend:8080;
}
# Redirect /old-page to /new-page
location /old-page {
return 301 /new-page;
}
}Add Security Headers:
server {
# Add security headers to all responses
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "geolocation=(), microphone=()";
location / {
proxy_pass http://backend:8080;
}
}Rate Limiting
Limit requests per IP:
# Define rate limit zone
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
listen 80;
location /api/ {
# Max 10 requests/second per IP
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend:8080;
}
}HAProxy as Reverse Proxy
global
maxconn 4096
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend web_frontend
bind *:80
# Route based on path
acl is_api path_beg /api/
acl is_static path_beg /static/
use_backend api_backend if is_api
use_backend static_backend if is_static
default_backend web_backend
backend web_backend
balance roundrobin
server web1 192.168.1.101:8080 check
server web2 192.168.1.102:8080 check
backend api_backend
balance leastconn
server api1 192.168.1.201:8080 check
server api2 192.168.1.202:8080 check
backend static_backend
balance roundrobin
server static1 192.168.1.251:9000 check
Caching Strategies
Cache Busting Strategy:
Version in URL: /static/app.v123.js
Browser caches forever
Update code: /static/app.v124.js
→ New URL, new request to server
Cache Conditional:
# Cache for 1 hour
proxy_cache_valid 200 1h;
# If backend returns 304 Not Modified
proxy_cache_valid "http_code 304" 1h;
# Check If-Modified-Since header
if-modified-since: conditional requestTroubleshooting
"Backend can't see real client IP"
Add to reverse proxy config:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
App reads X-Real-IP or X-Forwarded-For header"Responses cached incorrectly"
1. Check Cache-Control headers from backend
curl -i http://backend:8080/api/data
→ Look for: Cache-Control: no-cache, no-store
2. Disable caching for dynamic content
proxy_no_cache $skip_cache;
proxy_cache_bypass $skip_cache;
"Backend connection timeout"
Increase timeouts:
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;Key Concepts
- Reverse Proxy = Sits in front of backend servers
- Forward Proxy = Sits in front of clients
- Load Balancing = Distribute across backends
- Caching = Store responses to reduce backend load
- SSL Termination = Encryption at proxy, not backend
- Content-based Routing = Route by URL, hostname
- Rate Limiting = Protect against abuse
- Security Headers = Protect against web attacks
- Reverse proxy is transparent to clients
- Always forward real client IP to backend