Reverse Proxy Patterns
A reverse proxy sits between clients and backend servers, accepting client requests and forwarding to backends. It's a load balancer's close relative — most reverse proxies do load balancing, but they often do more.
This page covers what reverse proxies do beyond load balancing and the architectural roles they play.
The roles
A reverse proxy can do many things. Common patterns:
Load balancing
Distribute requests across backend servers. See [LoadBalancingStrategies](LoadBalancingStrategies).
TLS termination
Decrypt incoming HTTPS at the proxy; forward HTTP to backends. Centralizes cert management.
Caching
Cache responses; serve subsequent identical requests without hitting backends. Significant load reduction for cacheable content.
Compression
Gzip or Brotli responses. Saves bandwidth.
Authentication / authorization
Check credentials at the proxy. Backends only see authenticated requests.
Rate limiting
Limit requests per client. Protect backends from abuse.
Request rewriting
Modify URLs, headers, or body before forwarding. Useful for migrations or legacy support.
Header injection
Add headers backends need (X-Forwarded-For, request IDs, geo-location).
Static file serving
Serve static assets directly without forwarding to backends.
Common reverse proxies
Nginx
The dominant open-source choice. Powerful; widely understood; well-documented. Uses event-driven architecture; very high concurrency.
```nginx
server {
listen 443 ssl;
server_name api.example.com;
location / {
proxy_pass http://backend_pool;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static/ {
root /var/www;
}
}
```
HAProxy
Strong on L4 and L7. Slightly more technical config than Nginx. Excellent at extreme scale.
Envoy
Modern, cloud-native. Designed for microservices and service mesh. More complex; more features.
Apache (httpd)
Older; still used. Less common as a reverse proxy than Nginx.
Caddy
Modern; automatic HTTPS via Let's Encrypt. Simpler config than Nginx. Good for smaller deployments.
Cloud-managed
ALB, Cloud Load Balancing, Application Gateway. Managed; less to operate.
Architectural patterns
Single reverse proxy per host
Each backend host has its own reverse proxy. The proxy serves static files, terminates TLS, and forwards to a local app.
Frontend / API gateway
A single reverse proxy in front of all backend services. Routes by path:
- `/api/auth/*` → auth-service
- `/api/orders/*` → order-service
- `/static/*` → static file server
This is the "API gateway" pattern. Centralizes cross-cutting concerns.
Sidecar proxy
A proxy runs alongside each application instance (typical in Istio, Linkerd service meshes). All inbound and outbound traffic goes through it.
Pros: cross-cutting concerns moved out of app code; consistent across languages.
Cons: more processes; latency overhead; operational complexity.
Edge proxy
The proxy is at the network edge (CDN POPs, regional edges). Cloudflare, AWS CloudFront with Lambda@Edge.
Edge proxies do many of the same things as backend proxies but closer to users.
TLS termination patterns
Terminate at proxy; HTTP to backend
```
Client (HTTPS) → Proxy → Backend (HTTP)
```
Simple. Backends don't deal with TLS. Backend network must be trusted.
Re-encrypt at proxy
```
Client (HTTPS) → Proxy → Backend (HTTPS)
```
Backend traffic also encrypted. Higher CPU; more cert management.
For internal traffic in a trusted VPC, terminate at proxy is fine. For zero-trust, re-encrypt.
TLS passthrough
Proxy doesn't terminate; just routes encrypted traffic. The proxy can route based on SNI but can't inspect content.
Used when content inspection is not desired (end-to-end encryption mandate).
Caching at the reverse proxy
```nginx
proxy_cache_path /var/cache levels=1:2 keys_zone=mycache:10m;
location / {
proxy_cache mycache;
proxy_cache_valid 200 5m;
proxy_cache_use_stale error timeout updating;
proxy_pass http://backend;
}
```
Pattern: cache 200 responses for 5 minutes; serve stale responses if backend is down.
For cacheable content, this dramatically reduces backend load. Be careful not to cache user-specific or authenticated content.
Request manipulation
Header injection
```nginx
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
```
Critical for backends to know the original client IP.
URL rewriting
```nginx
location /old-path {
rewrite ^/old-path/(.*) /new-path/$1 break;
proxy_pass http://backend;
}
```
Useful for migrations.
Common failure patterns
- **Misconfigured X-Forwarded-For.** Backend sees wrong client IP.
- **Wrong cache configuration.** User A sees user B's data.
- **Tight timeouts on backend that's slow.** Cascading 504s.
- **No health checks.** Dead backends still receive traffic.
- **Reverse proxy as single point of failure.** Use HA setup.
- **Insufficient connection limits.** Resource exhaustion.
Further Reading
- [LoadBalancingStrategies](LoadBalancingStrategies) — Closely related
- [CdnArchitecture](CdnArchitecture) — Edge-level reverse proxy
- [WebApplicationFirewalls](WebApplicationFirewalls) — Often run as part of reverse proxy
- [TcpIpFundamentals](TcpIpFundamentals) — Underneath
- [Networking Hub](NetworkingHub) — Cluster index