Reverse Proxy
IntermediateA reverse proxy sits in front of backend servers and forwards client requests to them — providing SSL termination, caching, compression, rate limiting, and shielding internal server topology from external clients.
Overview
A forward proxy acts on behalf of clients (VPN, corporate internet filtering). A reverse proxy acts on behalf of servers — clients send requests to the proxy's IP/domain, and the proxy decides which backend to contact. Nginx and HAProxy are the dominant open-source reverse proxies. In cloud architectures, reverse proxies appear as API Gateways, CDN edge nodes, and Ingress Controllers in Kubernetes. A reverse proxy decouples the client from backend topology: you can add, remove, or replace backends without clients noticing.
Forward Proxy vs Reverse Proxy
The key distinction is whose behalf the proxy acts on. A forward proxy is configured by clients — often transparent to servers. A reverse proxy is configured by server operators — often transparent to clients.
// Forward Proxy (client-side):
// Client ──▶ [Forward Proxy] ──▶ Internet
// Use cases:
// - Corporate internet filtering (block social media, log traffic)
// - Anonymisation (hide client IP from servers)
// - VPN/tunnelling (bypass geo-restrictions)
// Server sees: proxy's IP, not client's IP
// Reverse Proxy (server-side):
// Client ──▶ [Reverse Proxy] ──▶ Backend Server(s)
// Use cases:
// - SSL termination
// - Load balancing (Nginx upstream)
// - Caching static content
// - Rate limiting and security
// - Serve multiple apps on one IP (virtual hosts)
// Client sees: proxy's domain (aicancode.org), not backend's internal IP (10.0.1.5)
// Virtual hosting (multiple apps, one IP/port):
// nginx:
server { server_name api.aicancode.org; proxy_pass http://api_backend; }
server { server_name www.aicancode.org; proxy_pass http://nextjs_backend; }Nginx as Reverse Proxy
Nginx is event-driven and handles tens of thousands of concurrent connections with minimal memory. Its reverse proxy configuration proxies requests to upstream backends while adding headers, enabling caching, and buffering responses.
# Nginx reverse proxy — full production config
upstream nextjs_app {
server 127.0.0.1:3000;
keepalive 32; # keep connections to backend alive
}
server {
listen 443 ssl http2;
server_name aicancode.org www.aicancode.org;
ssl_certificate /etc/letsencrypt/live/aicancode.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/aicancode.org/privkey.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
# Compression
gzip on;
gzip_types text/plain application/json application/javascript text/css;
location / {
proxy_pass http://nextjs_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; # pass real client IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
proxy_buffering on;
}
# Cache static assets at edge
location ~* \.(js|css|png|jpg|woff2)$ {
proxy_pass http://nextjs_app;
proxy_cache_valid 200 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
}
}
# HTTP → HTTPS redirect
server { listen 80; return 301 https://$host$request_uri; }Caching at the Reverse Proxy
A reverse proxy can cache backend responses — serving subsequent identical requests directly without hitting the backend. This dramatically reduces origin load for read-heavy endpoints. Cache keys are typically based on URL + selected headers. Cache invalidation must be considered: either TTL-based or explicit purge.
# Nginx proxy cache config:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m
max_size=1g inactive=60m use_temp_path=off;
server {
location /api/v1/courses {
proxy_cache api_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 5m; # cache 200 responses for 5 minutes
proxy_cache_valid 404 1m;
proxy_cache_bypass $http_x_no_cache; # bypass cache if header set
add_header X-Cache-Status $upstream_cache_status; # HIT / MISS / BYPASS
proxy_pass http://api_backend;
}
# Purge cache on deploy (nginx-cache-purge module):
location ~ /purge(/.*) {
allow 127.0.0.1;
deny all;
proxy_cache_purge api_cache "$scheme$request_method$host$1";
}
}Key Points to Remember
- 1A reverse proxy decouples client-facing endpoints from internal backend topology — backends can change without affecting clients.
- 2Nginx handles tens of thousands of concurrent connections with minimal memory using an event-driven, non-blocking architecture.
- 3Always pass X-Forwarded-For and X-Real-IP headers so backends know the actual client IP, not the proxy's IP.
- 4Proxy-level caching reduces backend load for read-heavy endpoints — combine with immutable Cache-Control headers for static assets.
- 5Kubernetes Ingress Controllers (nginx-ingress, Traefik) are reverse proxies that route cluster-external traffic to services.
Interview Questions
Sign in to ask AriaWhat is the difference between a forward proxy and a reverse proxy?
Why do you need to pass X-Forwarded-For when using a reverse proxy?
How does Nginx serve multiple domains/apps from a single IP and port?
How would you implement caching at the reverse proxy level and when should you invalidate it?
What is a Kubernetes Ingress Controller and how does it relate to a reverse proxy?
Ask Aria about Reverse Proxy
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.