Why This Matters
When a client sends a request to your web application, it rarely talks to your application server directly. Instead, a reverse proxy sits between the client and your backend servers. It receives the request, decides where to forward it, and returns the response to the client. The client never knows which backend server actually handled the request.
Reverse proxies are the Swiss Army knife of web infrastructure. They handle SSL termination so your backend does not need to manage certificates. They cache responses, compress data, rate-limit abusive clients, and route requests to different services based on URL paths. Nearly every production web application sits behind a reverse proxy like Nginx, Caddy, or a cloud-managed API gateway.
Define Terms
Visual Model
The full process at a glance. Click Start tour to walk through each step.
A reverse proxy terminates TLS, routes requests by URL path, and forwards them to different backend services.
Code Example
// Simulating reverse proxy header handling
// When a reverse proxy forwards a request, it adds headers
// so the backend knows the original client details.
function addProxyHeaders(originalRequest, backendRequest) {
// Preserve the original client IP
backendRequest.headers["X-Forwarded-For"] = originalRequest.clientIp;
// Preserve the original protocol (https)
backendRequest.headers["X-Forwarded-Proto"] = originalRequest.protocol;
// Preserve the original host
backendRequest.headers["X-Forwarded-Host"] = originalRequest.host;
return backendRequest;
}
// Simple path-based routing
function routeRequest(path) {
if (path.startsWith("/api")) {
return "http://api-service:3000";
} else if (path.startsWith("/assets")) {
return "http://static-service:8080";
} else {
return "http://web-app:4000";
}
}
console.log(routeRequest("/api/users"));
// http://api-service:3000
console.log(routeRequest("/assets/logo.png"));
// http://static-service:8080
console.log(routeRequest("/dashboard"));
// http://web-app:4000
// Extracting real client IP from proxy headers
function getClientIp(request) {
// X-Forwarded-For may contain multiple IPs if there are chained proxies
const forwarded = request.headers["x-forwarded-for"];
if (forwarded) {
// First IP is the original client
return forwarded.split(",")[0].trim();
}
return request.connection.remoteAddress;
}
const mockReq = {
headers: { "x-forwarded-for": "203.0.113.50, 70.41.3.18" },
connection: { remoteAddress: "10.0.0.1" }
};
console.log(getClientIp(mockReq)); // 203.0.113.50Interactive Experiment
Try these exercises:
- Extend the
routeRequestfunction to handle additional paths like/authand/ws(WebSocket). - Implement a simple response caching layer: if the same URL is requested twice, return the cached response.
- What happens if the X-Forwarded-For header has three IPs? Which one is the real client? Which are proxies?
- How would you add rate limiting to the reverse proxy? Sketch out the logic (track request counts per IP per time window).
Quick Quiz
Coding Challenge
Write a function called `createRouter` that takes a routing table (an object mapping path prefixes to backend URLs) and returns a function. The returned function accepts a request path and returns the matching backend URL. It should match the longest matching prefix. If no prefix matches, return 'http://default-service:80'.
Real-World Usage
Reverse proxies are a fundamental building block of web infrastructure:
- Nginx: The most popular reverse proxy, used by over 30% of all websites. Handles SSL termination, static file serving, caching, and load balancing.
- API gateways: Kong, AWS API Gateway, and Traefik act as reverse proxies that add authentication, rate limiting, and request transformation for microservices.
- CDNs: Cloudflare and AWS CloudFront are globally distributed reverse proxies that cache content close to users for faster delivery.
- Service meshes: In Kubernetes, sidecar proxies (Envoy, Istio) act as reverse proxies for each microservice, handling mutual TLS, retries, and observability.
- SSL certificates: Let's Encrypt certificates are typically configured on the reverse proxy, not on individual application servers.