Reverse proxies are one of those infrastructure components that feel optional until the moment you need them — and then you realise you should have had one all along. They centralise TLS termination, header manipulation, caching, rate limiting, and routing into a single layer that backends never have to think about.
What changed in 2026
- Caddy is widely adopted. Its automatic HTTPS (via ACME/Let's Encrypt) and JSON/Caddyfile configuration reduced certificate management from a recurring task to a zero-touch operation.
- Envoy replaced Nginx in most Kubernetes clusters. Istio, Contour, and Emissary all use Envoy as the data plane. Understanding Envoy's xDS API is now infrastructure-engineer knowledge.
- AI-driven WAFs. Cloudflare and AWS WAF now use ML models to detect anomalous traffic — traditional rule-based WAFs are being supplemented with behavioural analysis.
- HTTP/3 (QUIC) at the edge. Cloudflare, Nginx, and Caddy support QUIC. Clients connecting over lossy networks (mobile) see 20–40 % lower latency at connection setup.
What a reverse proxy does
Client → [Reverse Proxy] → Backend Server A
→ Backend Server B
→ Backend Server C
The proxy receives the request, applies policies (TLS, auth, rate limit, routing), and forwards to the appropriate backend. The backend's response travels the same path back.
Key capabilities:
| Capability |
Benefit |
| TLS termination |
Backends handle plain HTTP; certs in one place |
| Load balancing |
Distribute across multiple backends |
| Caching |
Serve repeated responses without hitting backends |
| Rate limiting |
Protect backends from traffic spikes |
| Header manipulation |
Add X-Real-IP, strip internal headers |
| Path-based routing |
/api/* → API service, /app/* → frontend |
| Authentication |
JWT validation, OAuth at the proxy layer |
Nginx reverse proxy
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Rate limiting (define zone in http block)
# limit_req zone=api burst=20 nodelay;
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
# Timeouts
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 10s;
}
location /static/ {
root /var/www;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Caddy reverse proxy
Caddy's Caddyfile is dramatically simpler and handles HTTPS automatically:
api.example.com {
reverse_proxy localhost:3000 {
header_up X-Real-IP {remote_host}
transport http {
dial_timeout 5s
response_header_timeout 30s
}
}
}
# Path-based routing
app.example.com {
handle /api/* {
reverse_proxy localhost:3000
}
handle {
reverse_proxy localhost:5173
}
}
Run caddy run --config Caddyfile and HTTPS is live, auto-renewing forever.
Envoy for service-to-service traffic
Envoy uses a dynamic configuration API (xDS) rather than static config files:
# envoy.yaml (static bootstrap example)
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 8080 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
route_config:
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: backend_service }
clusters:
- name: backend_service
connect_timeout: 5s
load_assignment:
cluster_name: backend_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 127.0.0.1, port_value: 3000 }
In Kubernetes, you rarely write Envoy config directly — Istio's control plane generates it from VirtualService and DestinationRule resources.
Reverse proxy vs API gateway vs load balancer
| Tool |
Primary role |
Typical layer |
| Reverse proxy |
Route, terminate TLS, cache |
North-South edge |
| Load balancer |
Distribute traffic across backends |
North-South or East-West |
| API gateway |
Auth, rate limit, API versioning, billing |
North-South API entry point |
| Service mesh proxy |
mTLS, observability, retries |
East-West (service to service) |
The boundaries blur — Nginx can be all four. The distinction is conceptual, not technical.
How to pick
- Simple single-server app with HTTPS? → Caddy (zero config, auto-certs).
- High-traffic, fine-tuned config? → Nginx.
- Kubernetes service mesh? → Envoy (via Istio or Contour).
- Managed, zero-ops? → Cloudflare Tunnel + Workers, or a cloud ALB.
- Need a full API gateway? → Kong, Traefik, or AWS API Gateway.
Common mistakes
Not setting timeouts. A reverse proxy without proxy_read_timeout holds connections open for the default (60 s in Nginx) on slow/stuck backends. Set aggressive timeouts and rely on retries for transient errors.
Forwarding the wrong Host header. Some backends use the Host header for routing. Forgetting proxy_set_header Host $host causes 404s from virtual-hosted backends.
Caching dynamic API responses. Nginx's proxy_cache will cache a GET /api/user/1 response and serve it stale to every user. Only cache explicitly: static assets and GET endpoints with Cache-Control: public.
No X-Forwarded-For handling in the backend. Backends that log REMOTE_ADDR will log the proxy's IP, not the real client IP. Read X-Forwarded-For (or use the PROXY protocol) and validate it.
What to skip
- Apache httpd as a reverse proxy in new projects — Nginx and Caddy have better performance and simpler config; Apache's rewrite rules are hard to maintain.
- Multiple reverse proxies in series without a clear reason — each hop adds latency and complexity. One well-configured proxy at the edge is enough for most architectures.
- Disabling TLS between proxy and backend "for speed" — use mTLS or at minimum enforce plain HTTP only on a private VPC interface, never on the public internet.
FAQ
Is Cloudflare a reverse proxy?
Yes — when you put your domain behind Cloudflare, all traffic passes through Cloudflare's global network before reaching your origin. It acts as a reverse proxy with caching, WAF, and DDoS protection built in.
What is TLS termination?
The reverse proxy decrypts incoming HTTPS traffic, inspects and routes the request as plain HTTP, then (optionally) re-encrypts before forwarding to the backend. This offloads TLS from backend servers.
How is a reverse proxy different from a forward proxy?
A forward proxy sits in front of clients (e.g. a corporate proxy that intercepts outbound employee traffic). A reverse proxy sits in front of servers (intercepts inbound client traffic). Clients know about forward proxies; they typically do not know about reverse proxies.
Can I use Nginx as both a web server and a reverse proxy?
Yes — this is the most common Nginx configuration. Nginx serves static files directly from disk and proxies dynamic requests to an app server (Node, Gunicorn, etc).
Where to go next