# Proxy and Reverse Proxy

## 1. Same Mechanism, Opposite Direction

A proxy is any intermediary that sits between a client and a server, forwarding requests and responses on someone's behalf. "Forward proxy" and "reverse proxy" describe the exact same relaying mechanism — the only difference is **which side it's hiding**:

```
FORWARD PROXY  (hides the CLIENT from the server)

  Client A  --\
  Client B  ---> [ Forward Proxy ] ---> Server
  Client C  --/

  The server sees ONE proxy IP. It has no idea which of A/B/C actually asked.


REVERSE PROXY  (hides the SERVER(s) from the client)

                              /--> Backend 1
  Client ---> [ Reverse Proxy ]---> Backend 2
                              \--> Backend 3

  The client sees ONE proxy IP/domain. It has no idea which backend actually answered.
```

A forward proxy is configured and known to the **client** (corporate egress filtering, a VPN, an anonymizing proxy) and is typically invisible to the server. A reverse proxy is configured and known to the **server operator** (nginx, an API gateway, a CDN edge) and is typically invisible to the client, who believes they're talking directly to "the site."

![A forward proxy sits on the client side and controls outbound traffic, while a reverse proxy sits in front of servers handling TLS, load balancing and caching](diagrams/01-forward-vs-reverse.svg)

## 2. What a Reverse Proxy Actually Does

A reverse proxy is rarely just a dumb relay — it's usually doing several jobs at once, all invisibly to the client:

| Responsibility | What it means |
|---|---|
| TLS termination | client's HTTPS connection ends at the proxy; proxy-to-backend traffic may be plain HTTP on a trusted internal network |
| Load balancing | spreads requests across multiple backend instances (round-robin, least-connections, etc.) |
| Routing | sends `/api/*` to one backend, `/*` (everything else) to another, based on path or `Host` |
| Caching | can serve a cached response itself without ever forwarding to a backend (see `05-caching_and_compression`) |
| Compression | can compress an uncompressed backend response before it reaches the client |
| Rate limiting / auth | can reject or throttle requests before a backend ever sees them |

```nginx
# nginx reverse proxy: path-based routing to two backends, one upstream
upstream api_backend {
    server 10.0.0.11:4000;
    server 10.0.0.12:4000;   # load-balanced across both
}

server {
    listen 443 ssl;
    server_name northwind.dev;

    location /api/ {
        proxy_pass http://api_backend;
    }

    location / {
        proxy_pass http://10.0.0.20:3000;   # web app backend
    }
}
```

## 3. The Problem: the Backend Loses the Real Client

Once a reverse proxy is in the path, every request the backend receives has the proxy's own IP as its TCP source address — from the backend's point of view, *every* request looks like it came from the same one machine:

```
Client (203.0.113.7) -> Reverse Proxy (10.0.0.1) -> Backend

Backend's connection info: remote address = 10.0.0.1  (the proxy, not the client!)
```

This breaks anything that depends on the real client IP: per-IP rate limiting, geolocation, abuse logging, "last login IP" — all of it, unless the proxy explicitly forwards the original information in headers.

## 4. Forwarding Headers

```http
X-Forwarded-For: 203.0.113.7
X-Forwarded-Proto: https
X-Forwarded-Host: northwind.dev
Via: 1.1 nginx-edge-01
```

| Header | Carries |
|---|---|
| `X-Forwarded-For` | the original client IP (and, if there are multiple proxies in a chain, a comma-separated list, oldest first) |
| `X-Forwarded-Proto` | whether the *original* client connection was `http` or `https` — matters because TLS is often terminated at the proxy, so the backend's own connection may be plain `http` even though the real client used `https` |
| `X-Forwarded-Host` | the `Host` header the client originally sent, in case the proxy rewrites it before forwarding |
| `Via` | a general-purpose "this passed through proxy X" trace header, standardized (unlike the `X-Forwarded-*` family, which is a de facto convention, not an RFC standard — the standardized replacement is `Forwarded`, e.g. `Forwarded: for=203.0.113.7;proto=https`, though `X-Forwarded-*` remains far more common in practice) |

A backend must be explicitly configured to read `X-Forwarded-For` (or `Forwarded`) instead of the raw TCP source address wherever it wants the real client's IP — this is not automatic, and most web frameworks default to trusting the raw socket address only.

## 5. Proxy Chains and Trust

`X-Forwarded-For` can accumulate multiple hops:

```http
X-Forwarded-For: 203.0.113.7, 10.0.0.1, 10.0.0.2
                  ^ original    ^ proxy 1  ^ proxy 2 (closest to backend)
```

Each proxy in the chain is supposed to *append* its own view of the previous hop's address rather than overwrite the header — which means **the leftmost value is only trustworthy if every proxy in the chain is trusted to append correctly and never forge it**. A client can freely send their own fabricated `X-Forwarded-For` header on the original request; a correctly configured edge proxy should strip or overwrite any client-supplied value before appending the real connecting IP, otherwise the backend ends up trusting attacker-controlled input for exactly the kind of decision (rate limiting, geo-restriction, abuse blocking) this header exists to support.

## 6. The API Gateway Pattern

When a reverse proxy fronts multiple *independent* backend services behind one public domain, it's commonly called an API gateway — the routing responsibility from section 2 becomes the primary job:

```
Client -> https://northwind.dev/*

  /            -> web app backend
  /api/*       -> REST API backend
  /ws/*        -> WebSocket backend (see note below)
  /admin/*     -> internal admin backend, extra auth enforced at the gateway
```

A single public domain and a single TLS certificate front several independently deployable services, each of which can be scaled, deployed, or replaced without the client ever knowing the topology changed. WebSocket traffic needs explicit proxy support for the `Upgrade`/`Connection: upgrade` handshake (a plain HTTP reverse-proxy config that doesn't forward these headers will silently break WebSocket connections while ordinary HTTP routes through it fine).

## Common Pitfalls

1. **Confusing forward and reverse proxy direction.** The mechanism is identical; only "which party is anonymized" differs — mixing them up leads to configuring the wrong side's trust boundary.
2. **Trusting `X-Forwarded-For` without stripping client-supplied values first.** Any client can send a fake `X-Forwarded-For` on the original request; only the edge proxy's own appended value is trustworthy, and only if every proxy in the chain enforces this.
3. **Forgetting `X-Forwarded-Proto`.** A backend that redirects or generates absolute URLs based on its own (often plain-HTTP) connection to the proxy can emit `http://` links even though the real client is on `https://`, causing mixed-content errors or redirect loops.
4. **Not forwarding `Upgrade`/`Connection` headers for WebSocket routes.** Ordinary HTTP traffic works fine through a proxy misconfigured this way, masking the bug until a WebSocket-dependent feature is tested.
5. **Logging or rate-limiting on the raw TCP source address instead of the forwarded client IP.** Every request appears to come from the proxy, making per-client analysis meaningless without reading the forwarding headers.

## Quick Reference

```
Forward proxy  -> hides the CLIENT from the server; configured by/known to the client
Reverse proxy  -> hides the SERVER(s) from the client; configured by/known to the server operator

Reverse proxy jobs: TLS termination, load balancing, routing, caching, compression, rate limiting

X-Forwarded-For    -> original client IP (comma-separated chain if multiple proxies)
X-Forwarded-Proto  -> http/https of the ORIGINAL client connection (TLS may be terminated upstream)
X-Forwarded-Host   -> original Host header, if rewritten before forwarding
Via                -> standardized hop trace; Forwarded is the standardized alternative to X-Forwarded-*

Only trust X-Forwarded-For if every hop strips/overwrites client-supplied values before appending
API gateway -> reverse proxy fronting multiple independent backend services on one public domain
```
