system-design.io

Reverse Proxy vs. Forward Proxy: A Complete Guide for Software Engineers


Part 1: The Bridge — A Tale of Two Middlemen

Before we talk about servers, IP addresses, or HTTP headers, let’s walk into a building.

Imagine you work at a large corporation. You need to send a letter to an external vendor — but company policy says all outgoing mail must go through the corporate mail room. You hand your letter to the mail room, they stamp it with the company’s return address (not yours), and send it out. The vendor never knows which employee sent the letter. They just see the company.

That mail room? That’s a Forward Proxy.

Now flip it around. You’re a customer trying to reach the “CEO of MegaCorp.” You don’t have his direct line. You call the front desk receptionist. She screens your call, routes it to the right executive assistant, who passes it along. You, the caller, never know which server — sorry, which person — actually handled your request. You just hear a polished, unified voice from “MegaCorp.”

That receptionist? That’s a Reverse Proxy.

Both are middlemen. Both intercept traffic. But they serve fundamentally opposite sides of a conversation — and that distinction is the entire story we’re about to tell.


Part 2: What Even Is a Proxy? (The Atomic Unit)

Let’s start with the simplest possible concept before we add complexity.

At its most fundamental level, a proxy is any entity that acts on behalf of another. In networking, a proxy is a server that sits between two communicating parties and relays messages between them. Neither party needs to communicate directly with the other.


                    ┌──────────┐
    ┌────────┐        │          │        ┌─────────┐
│ Client │───────▶│  PROXY   │───────▶│ Server  │
└────────┘        │          │        └─────────┘
└──────────┘

Figure 1: The most basic proxy model. The client sends a request to the proxy, and the proxy forwards it to the server. The server’s response flows back the same way.

Why would we ever want this? Let’s follow the narrative:

The pain point: Two computers on the internet communicate directly using IP addresses. If client A wants to talk to server B, A must know B’s IP, and B can see A’s IP. This creates immediate problems:

Proxies solve all of these problems — but which problem they primarily solve depends on which direction the proxy is facing.


Part 3: The Forward Proxy — The Client’s Bodyguard

The Setup

A forward proxy (often just called a “proxy server”) operates on the client’s side of the equation. It is configured by — and primarily serves — the clients within a private network.

When you configure a forward proxy in your browser or OS, every web request you make goes to the proxy first. The proxy then forwards your request to the internet on your behalf.


Private Network                      Public Internet
┌─────────────────────────────┐
│                             │
│  ┌──────────┐               │
│  │ Client A │──┐            │
│  └──────────┘  │            │
│                │   ┌──────────────┐         ┌───────────┐
│  ┌──────────┐  ├──▶│   FORWARD    │────────▶│  Website  │
│  │ Client B │──┘   │    PROXY     │         └───────────┘
│  └──────────┘      └──────────────┘
│                         │
│  ┌──────────┐           │  (All traffic appears to
│  │ Client C │───────────┘   originate from the proxy,
│  └──────────┘               not individual clients)
└─────────────────────────────┘

Figure 2: A forward proxy sits at the edge of a private network. All client requests exit through it. External servers only ever see the proxy’s IP address — never the individual client IPs.

Notice in Figure 2 how three separate clients all funnel through a single exit point. This is the forward proxy’s defining characteristic: it hides the clients from the outside world.

Key Behaviors of a Forward Proxy

1. Client Anonymization

When Client A in Figure 2 visits https://example.com, the request arrives at the proxy. The proxy strips Client A’s IP address and sends the request using its own IP. The web server sees the proxy’s IP — not Client A’s. This is the foundation of many anonymization tools, VPNs, and Tor-adjacent technologies.

2. Access Control & Content Filtering

This is heavily used in corporate and school environments. The forward proxy inspects outgoing requests and can block access to restricted domains.

# Simplified conceptual model of a forward proxy decision engine

BLOCKED_DOMAINS = {"socialmedia.com", "gambling.com", "streaming.com"}

def forward_proxy_handler(client_request):
    target_domain = extract_domain(client_request.url)

    if target_domain in BLOCKED_DOMAINS:
        return HttpResponse(403, "Access Denied by Policy")

    # Forward request to the target server
    response = requests.get(
        client_request.url,
        headers={"X-Forwarded-For": PROXY_IP}  # Mask client IP
    )
    return response

3. Caching

A forward proxy can cache responses from commonly visited websites. If 200 employees all request the same JavaScript library from a CDN, the proxy can serve it from its local cache after the first request — dramatically reducing bandwidth.

4. Geo-Bypassing

Because the external server sees the proxy’s IP address (not the client’s), using a forward proxy located in another country effectively makes the client appear to be in that country. This is the mechanism behind most commercial VPN services and regional content unblockers.

The Forward Proxy in History

Forward proxies were one of the earliest forms of internet infrastructure optimization. In the early 1990s, when internet bandwidth was scarce and expensive, university networks and corporations deployed caching proxies (like Squid, still in use today) to reduce redundant bandwidth consumption. The caching use case dominated early forward proxy deployments before security and anonymization became the primary drivers.

The Critical Insight: Client Awareness

Here’s the detail that distinguishes a forward proxy from everything else: the client must know about the forward proxy and be explicitly configured to use it.

When you set up a forward proxy in your browser settings or system network preferences, you are deliberately routing your traffic through it. If the proxy is down and no bypass policy exists, you can’t reach the internet at all. The client is aware and dependent on the proxy.


Part 4: The Reverse Proxy — The Server’s Shield

Flipping the Problem

Now let’s approach the problem from the other direction.

You’re running a popular web application. Millions of users hit your servers every day. Your pain points look completely different:

The reverse proxy is the solution to all of these problems at once.

The Setup

A reverse proxy sits in front of your backend servers, facing the public internet. Clients send their requests to the reverse proxy (believing it to be the actual server), and the reverse proxy decides where to route each request internally.

  Public Internet                     Private Network
                                 ┌──────────────────────────┐
                                 │                          │
  ┌──────────┐                   │   ┌──────────────────┐   │
  │ Client A │──┐                │   │   App Server 1   │   │
  └──────────┘  │                │   └──────────────────┘   │
                │   ┌──────────────┐                        │
  ┌──────────┐  ├──▶│   REVERSE   │──▶┌──────────────────┐  │
  │ Client B │──┘   │    PROXY    │   │   App Server 2   │  │
  └──────────┘      └──────────────┘  └──────────────────┘  │
                │         │                                  │
  ┌──────────┐  │         └──▶┌──────────────────┐           │
  │ Client C │──┘             │   Static Assets  │          │
  └──────────┘                │     Server       │          │
                              └──────────────────┘          │
                                 └──────────────────────────┘

Figure 3: A reverse proxy receives all incoming traffic from the public internet. It then distributes requests to the appropriate backend server. Clients are completely unaware that multiple backend servers exist — they only ever see the reverse proxy’s address.

Notice in Figure 3 how clients perceive the reverse proxy as the server. They have no visibility into what lies behind it. This is the reverse proxy’s defining characteristic: it hides the servers from the outside world.

Key Behaviors of a Reverse Proxy

1. Load Balancing

This is arguably the most important function. When request volume exceeds what a single server can handle, the reverse proxy distributes incoming requests across a pool of backend servers using algorithms like:

# Simplified round-robin load balancer inside a reverse proxy

from itertools import cycle
import requests

BACKEND_SERVERS = [
    "http://app-server-1:8080",
    "http://app-server-2:8080",
    "http://app-server-3:8080",
]

server_pool = cycle(BACKEND_SERVERS)

def reverse_proxy_handler(client_request):
    # Pick next server in rotation
    target_server = next(server_pool)

    # Forward the request to the chosen backend
    response = requests.request(
        method=client_request.method,
        url=f"{target_server}{client_request.path}",
        headers=client_request.headers,
        data=client_request.body,
    )
    return response

2. SSL/TLS Termination

Managing SSL certificates on every backend server is expensive and operationally painful. The reverse proxy can terminate (decrypt) incoming HTTPS connections, communicate with backend servers over plain HTTP internally, and handle all certificate management centrally.

Client ──[HTTPS]──▶ Reverse Proxy ──[HTTP]──▶ Backend Server
         (encrypted)              (unencrypted, internal only)

This is called SSL offloading. It reduces the CPU burden on backend servers significantly since encryption/decryption is computationally expensive.

3. Caching Static Assets

A reverse proxy can cache static content (images, CSS, JS files) and serve them directly without burdening backend application servers. Tools like Nginx (one of the most popular reverse proxies) excel at this.

  ┌──────────┐       ┌─────────────────────────────────────┐
  │  Client  │──────▶│           REVERSE PROXY             │
  └──────────┘       │                                     │
                     │  ┌──────────────────────────────┐   │
                     │  │  Cache Layer                 │   │
                     │  │  - /logo.png ✓               │   │
                     │  │  - /styles.css ✓             │   │
                     │  │  - /api/users ✗ (miss)       │   │
                     │  └──────────────────────────────┘   │
                     │       │ Cache Miss                   │
                     └───────┼─────────────────────────────┘
                             ▼
                     ┌───────────────┐
                     │ Backend Server│
                     └───────────────┘

Figure 4: The reverse proxy serves cached resources directly (cache hit), only forwarding requests to the backend when the resource is not in the cache (cache miss). This dramatically reduces load on application servers.

4. DDoS Protection \& Rate Limiting

Because the reverse proxy is the single entry point for all external traffic, it can implement sophisticated rate limiting, IP blocking, and traffic filtering before any request ever reaches a backend server.

from collections import defaultdict
from time import time

# Track requests per IP
request_log = defaultdict(list)
RATE_LIMIT = 100       # max requests
TIME_WINDOW = 60       # seconds

def rate_limit_middleware(client_ip, request):
    now = time()
    # Keep only requests within the time window
    request_log[client_ip] = [
        t for t in request_log[client_ip]
        if now - t < TIME_WINDOW
    ]

    if len(request_log[client_ip]) >= RATE_LIMIT:
        return HttpResponse(429, "Too Many Requests")

    request_log[client_ip].append(now)
    return forward_to_backend(request)

5. Server Anonymization

Just as the forward proxy hides client IPs, the reverse proxy hides server IPs. Even if attackers know your domain name, they only ever resolve it to the reverse proxy’s IP. Your actual application servers are never directly reachable — they live in a private subnet.

6. Path-Based Routing

A reverse proxy can intelligently route requests based on URL patterns, enabling a microservices architecture to be exposed under a single domain:

https://myapp.com/api/*       ──▶  API Server (Flask/FastAPI)
https://myapp.com/static/*    ──▶  Static File Server (S3/Nginx)
https://myapp.com/ws/*        ──▶  WebSocket Server
https://myapp.com/*           ──▶  Main Web App Server

This is how companies like Netflix, Twitter, and Airbnb serve diverse workloads under a single domain without clients knowing anything about the underlying infrastructure.

The Critical Insight: Client Unawareness

Here’s the mirror-image of the forward proxy insight: the client does NOT know there is a reverse proxy. The client simply makes a request to api.yourcompany.com — completely unaware that this resolves to a reverse proxy, which then fans out to 50 backend microservices.


Part 5: The Side-by-Side (Building the Full Picture)

Now that we understand each concept individually, let’s layer them together with a unified diagram:

                          THE INTERNET
                               │
                               │
  ┌────────────────────────────┼──────────────────────────────┐
  │ CORP NETWORK               │                              │
  │                            ▼                              │
  │  ┌──────────┐    ┌──────────────────┐                    │
  │  │Employee A│───▶│  FORWARD PROXY   │                    │
  │  └──────────┘    │  (Corp Firewall) │                    │
  │                  └────────┬─────────┘                    │
  │  ┌──────────┐             │ Requests go OUT              │
  │  │Employee B│─────────────┘                              │
  │  └──────────┘                                            │
  └──────────────────────────────────────────────────────────┘

                    ─── ─── ─── Public Internet ─── ─── ───

  ┌──────────────────────────────────────────────────────────┐
  │ VENDOR / STARTUP SERVER NETWORK                          │
  │                                                          │
  │           Requests come IN                               │
  │                  │                                       │
  │   ┌──────────────▼──────────────┐                       │
  │   │      REVERSE PROXY          │                       │
  │   │    (Nginx / HAProxy)        │                       │
  │   └────────────┬────────────────┘                       │
  │                │                                         │
  │    ┌───────────┼───────────┐                            │
  │    ▼           ▼           ▼                            │
  │ ┌──────┐  ┌──────┐   ┌──────┐                          │
  │ │App 1 │  │App 2 │   │App 3 │  (Private subnet)        │
  │ └──────┘  └──────┘   └──────┘                          │
  └──────────────────────────────────────────────────────────┘

Figure 5: The complete picture. On the left (client side), a corporate forward proxy controls what employees can access on the internet. On the right (server side), a reverse proxy controls how external users reach backend services. Both are middlemen — but they serve opposite constituencies.

The Definitive Comparison Table

Dimension Forward Proxy Reverse Proxy
Serves Clients (outbound traffic) Servers (inbound traffic)
Hides Client identities from servers Server identities from clients
Client Configuration Required (client must configure it) Not required (transparent to client)
Typical Deployer IT department / end-users DevOps / Platform team
Primary Use Cases Anonymization, content filtering, caching, geo-bypass Load balancing, SSL termination, DDoS protection, routing
Direction Inside → Outside Outside → Inside
Popular Tools Squid, Charles Proxy, Privoxy Nginx, HAProxy, Envoy, AWS ALB
Who knows it exists? The client (it’s configured there) Nobody on the client side

Part 6: Deep Dive — Real-World Implementations

Nginx as a Reverse Proxy (The Industry Standard)

Nginx (pronounced “engine-x”) is the most widely deployed reverse proxy and web server in the world. Let’s look at how a basic reverse proxy configuration works conceptually:

# Conceptual Python equivalent of what Nginx does for reverse proxying

import http.server
import urllib.request

BACKEND_SERVERS = {
    "/api/": "http://localhost:5001",
    "/auth/": "http://localhost:5002",
    "/static/": "http://localhost:5003",
}

class ReverseProxyHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        # Determine which backend should handle this path
        target_base = self.route_request(self.path)

        if not target_base:
            self.send_error(404)
            return

        # Forward request to the appropriate backend
        target_url = target_base + self.path
        with urllib.request.urlopen(target_url) as response:
            content = response.read()

        self.send_response(200)
        self.end_headers()
        self.wfile.write(content)

    def route_request(self, path):
        for prefix, backend in BACKEND_SERVERS.items():
            if path.startswith(prefix):
                return backend
        return BACKEND_SERVERS.get("/static/")  # Default fallback

This is the conceptual core of path-based routing. Nginx expresses this in its config file; what matters is the mental model: one entry point, multiple possible destinations.

The Health Check Problem

Here’s a real engineering challenge that emerges with reverse proxies: what happens when a backend server goes down?

If Client B’s request gets routed to App Server 2 (which is currently crashed), they’ll receive a cryptic error. The reverse proxy needs to continuously monitor the health of each backend and remove unhealthy servers from the pool.

  ┌──────────────────────────────────────────┐
  │           REVERSE PROXY                  │
  │                                          │
  │   ┌──────────────────────────────────┐   │
  │   │   Health Check Monitor           │   │
  │   │   Every 10 seconds:              │   │
  │   │   - Ping App Server 1  ✓ ALIVE   │   │
  │   │   - Ping App Server 2  ✗ DEAD    │   │
  │   │   - Ping App Server 3  ✓ ALIVE   │   │
  │   └──────────────────────────────────┘   │
  │                                          │
  │   Active Pool: [Server 1, Server 3]      │
  │   (Server 2 removed until it recovers)  │
  └──────────────────────────────────────────┘

Figure 6: The reverse proxy’s health check system maintains a list of “alive” backend servers. When a server fails, it’s removed from rotation. When it recovers, it’s added back — all without any client or backend configuration change.

import threading
import time
import requests

class HealthChecker:
    def __init__(self, servers, check_interval=10):
        self.all_servers = servers
        self.healthy_servers = list(servers)
        self.interval = check_interval

    def check_health(self):
        while True:
            healthy = []
            for server in self.all_servers:
                try:
                    r = requests.get(f"{server}/health", timeout=2)
                    if r.status_code == 200:
                        healthy.append(server)
                except requests.exceptions.RequestException:
                    print(f"[WARN] {server} is DOWN — removing from pool")

            self.healthy_servers = healthy
            time.sleep(self.interval)

    def start(self):
        t = threading.Thread(target=self.check_health, daemon=True)
        t.start()

Forward Proxy: Corporate Use Case Deep Dive

Let’s walk through a complete corporate scenario. A company wants to enforce these policies:

  1. Employees cannot access social media during work hours.
  2. All external requests must be logged for compliance.
  3. Commonly accessed documentation sites should be cached. ```python import logging from datetime import datetime from functools import lru_cache

BLOCKED_SITES = {“facebook.com”, “twitter.com”, “reddit.com”, “tiktok.com”} WORK_HOURS = range(9, 18) # 9 AM to 6 PM

logging.basicConfig(filename=’proxy_access.log’, level=logging.INFO)

@lru_cache(maxsize=512) def fetch_and_cache(url: str) -> bytes: “"”Cache responses for static documentation.””” response = requests.get(url) return response.content

def corporate_forward_proxy(client_ip: str, request): url = request.url domain = extract_domain(url) hour = datetime.now().hour

# Log all requests for compliance
logging.info(f"{datetime.now()} | {client_ip} | {url}")

# Policy 1: Block social media during work hours
if domain in BLOCKED_SITES and hour in WORK_HOURS:
    return HttpResponse(403, body="Access blocked by company policy.")

# Policy 2: Serve cached docs
if "docs." in domain or domain.endswith(".readthedocs.io"):
    cached_content = fetch_and_cache(url)
    return HttpResponse(200, body=cached_content)

# Default: Forward the request
return forward_request(request) ```

This is a simplified but architecturally faithful representation of how enterprise forward proxies like Squid or Zscaler operate.


Part 7: The Gray Area — Where They Overlap

We’ve drawn clean lines, but real-world infrastructure is messier. There are scenarios where a single server can act as both.

Scenario: The Engineering Laptop

You’re an engineer working from a coffee shop. Your company mandates all traffic runs through a corporate VPN (forward proxy). Simultaneously, the company’s cloud infrastructure uses Nginx as a reverse proxy in front of all services. Your request passes through both:

Your Laptop
    │
    ▼
Corporate VPN (Forward Proxy)   ← You configured this
    │
    ▼
Internet
    │
    ▼
Company's Nginx (Reverse Proxy) ← You didn't configure this
    │
    ▼
Backend API Server

The same request flows through two proxies — one serving your privacy as a client, one serving the company’s server infrastructure. They co-exist without conflict.

The API Gateway: A Reverse Proxy on Steroids

Modern API Gateways (like Kong, AWS API Gateway, or Apigee) are reverse proxies with superpowers layered on top:

  Client Request
       │
       ▼
  ┌─────────────────────────────────────────────┐
  │            API GATEWAY (Reverse Proxy+)      │
  │                                             │
  │  1. Authenticate JWT ──────────────▶ [Auth] │
  │  2. Rate limit check ───────────────▶ [OK]  │
  │  3. Log request ───────────────────▶ [Log]  │
  │  4. Transform payload (v1 → v2 format)      │
  │  5. Route to microservice                   │
  └──────────────────────────┬──────────────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
        User Service   Order Service  Payment Service

Figure 7: An API Gateway is a reverse proxy enriched with middleware capabilities. Each incoming request passes through a pipeline of cross-cutting concerns before reaching its target microservice. This pattern is the backbone of virtually every large-scale SaaS platform.


Part 8: Interview Patterns — What They Actually Ask

Since you’re preparing for technical interviews, let’s map these concepts to the questions you’re most likely to encounter.

Common Interview Questions \& How to Frame Answers

“Design a system that handles 10 million users per day.”

Your answer should naturally introduce a reverse proxy (load balancer) as a core component. The structure:

Users ──▶ [DNS] ──▶ [Reverse Proxy / Load Balancer] ──▶ [App Servers]
                              │
                        [SSL Termination]
                        [Rate Limiting]
                        [Caching Layer]

“How would you prevent DDoS attacks on your API?”

“How does Netflix serve content to users in 190 countries?”

“A company wants to restrict employee internet access. What would you build?”

The One-Line Mental Models

Memorize these for whiteboard interviews:


Part 9: Categorizing by Use Case Intent

Rather than presenting a random laundry list of features, let’s group the use cases by intent. This is the framework that will help you reason about which type of proxy to deploy in any given situation.

Intent Group 1: Anonymization \& Privacy

Goal Proxy Type Mechanism
Hide client from web servers Forward Proxy Replaces client IP with proxy IP
Hide servers from clients Reverse Proxy Clients only see proxy address
User-level anonymization (Tor) Chain of Forward Proxies Multi-hop IP masking
API security (hide server IPs) Reverse Proxy DNS resolves to proxy, not server

Intent Group 2: Performance \& Scale

Goal Proxy Type Mechanism
Reduce bandwidth for repeated requests Forward Proxy Cache popular external content
Distribute load across servers Reverse Proxy Load balancing algorithms
Reduce SSL computation on app servers Reverse Proxy SSL termination / offloading
Serve static assets faster Reverse Proxy Edge caching

Intent Group 3: Security \& Policy Enforcement

Goal Proxy Type Mechanism
Block employee access to sites Forward Proxy Domain/URL filtering
Comply with data egress regulations Forward Proxy Log and inspect outbound traffic
Protect servers from DDoS Reverse Proxy Rate limiting, IP blocking at edge
Prevent direct server access Reverse Proxy Backend servers in private subnet

Part 10: The Modern Landscape

Service Meshes: Proxies All the Way Down

In modern microservices architectures (Kubernetes, etc.), the concept has evolved further. A service mesh (like Istio or Linkerd) deploys a lightweight reverse proxy as a sidecar next to every single microservice. These sidecar proxies handle all inter-service communication:

  ┌──────────────────────────┐     ┌──────────────────────────┐
  │  Service A               │     │  Service B               │
  │                          │     │                          │
  │  ┌────────┐ ┌─────────┐  │     │  ┌─────────┐ ┌────────┐ │
  │  │App Code│ │ Sidecar │◀─┼─────┼─▶│ Sidecar │ │App Code│ │
  │  │        │ │ Proxy   │  │     │  │  Proxy  │ │        │ │
  │  └────────┘ └─────────┘  │     │  └─────────┘ └────────┘ │
  └──────────────────────────┘     └──────────────────────────┘
                     ▲                          ▲
                     └──────────────────────────┘
                          mTLS encrypted tunnel

Figure 8: In a service mesh, each service gets its own sidecar proxy (typically Envoy). The app code never talks directly to another service — it talks to its local sidecar, which handles encryption, observability, retries, and routing. This is the reverse proxy concept applied at a microscopic, per-service granularity.

This is the evolutionary endpoint of the reverse proxy concept: instead of one proxy protecting many servers, we have one proxy protecting each individual service — turning the entire infrastructure into a programmable network fabric.

Cloud-Native Proxies

Cloud providers have productized both proxy types:

The underlying concepts remain identical to what we’ve studied — they’re just managed services wrapped around the same fundamental architecture.


Part 11: The Complete Mental Framework

We’ve covered a lot of ground. Let’s crystallize everything into a decision framework you can apply instantly:

  Question: "Should I use a Forward or Reverse Proxy here?"
                          │
            ┌─────────────┴──────────────┐
            ▼                            ▼
     "Who am I protecting?"        "Who configures it?"
            │                            │
     ┌──────┴───────┐            ┌───────┴───────┐
     ▼              ▼            ▼               ▼
  Clients        Servers      Clients         Nobody
  (outbound)     (inbound)    (aware)         (transparent)
     │              │            │               │
     ▼              ▼            ▼               ▼
  FORWARD        REVERSE      FORWARD         REVERSE
   PROXY          PROXY        PROXY           PROXY

Figure 9: A decision tree for proxy selection. Both paths lead to the same answer — this is by design. No matter which question you start from, you’ll arrive at the correct proxy type.

The Golden Summary Sentences

Keep these with you:

  1. A forward proxy stands between your users and the internet — the users know it’s there.
  2. A reverse proxy stands between the internet and your servers — nobody on the outside knows it’s there.
  3. Forward proxies hide clients. Reverse proxies hide servers.
  4. If the client must configure something, it’s a forward proxy. If the server team deploys something, it’s a reverse proxy.
  5. Modern infrastructure uses reverse proxies as the backbone of scalability: load balancing, SSL termination, DDoS protection, and microservice routing all flow through them.

```