Proxy servers
Tell a forward proxy from a reverse one by whose behalf it acts, price the hop in round trips and connections, and keep the client IP from vanishing.
Proxy, reverse proxy, load balancer and API gateway overlap enough that candidates use them interchangeably, and interviewers notice. The distinction is one question: whose behalf is the intermediary acting on, the client's or the server's?
Whose behalf, and what that decides
Both sit between a client and a server, terminating one connection and opening another, so position tells you nothing. Who deployed the box does.
A forward proxy belongs to whoever owns the clients: route 50 office machines through one and outbound traffic is filtered, logged and cached in one place, while every destination sees one source address instead of 50.
A reverse proxy belongs to whoever owns the servers: the public hostname resolves to it, and it picks the backend that answers — spreading requests across a pool, returning what it holds cached, compressing responses, serving static files without waking an application server.
What the extra hop buys
A reverse proxy adds a network hop, so it has to pay for itself. Two mechanisms do, and both are countable.
The first is where the handshakes happen. HTTPS costs a TCP round trip and a TLS 1.3 round trip before a request byte moves, and Mumbai to Virginia is about 200 ms on the latency ladder. A direct call pays 2 × 200 = 400 ms of setup plus a 200 ms request: 600 ms. Move them to a reverse proxy 20 ms away and setup is 2 × 20 = 40 ms; the proxy holds a warm connection to the origin, so the request pays 20 ms to the proxy and 200 ms behind it: 40 + 20 + 200 = 260 ms, no application change. A CDN makes that argument at 400 locations.
The second is connection fan-in. Clients hold connections open while idle; backends care about requests in flight. Little's Law separates the two: concurrency = arrival rate × latency, so 500 QPS against a backend answering in 200 ms is 500 × 0.2 = 100 requests in flight. Whether 2,000 or 20,000 browsers are attached, 100 or so pooled connections carry them — size that pool from client count instead and you get thousands of idle server-side connections.
The client IP disappears, and something breaks
The moment a layer 7 reverse proxy terminates the connection, every backend sees one source address: the proxy's. Anything keyed on the remote address silently changes meaning.
The sharpest version is rate limiting: a limiter allowing 100 requests per minute per IP now holds one bucket for the whole user base, and at 500 QPS it empties in under a second, so everyone gets a 429 for the rest of the minute. Access logs go the same way, one address per line.
The fix is X-Forwarded-For (or the standard Forwarded header), with a condition attached. Trust it only when the request arrived from your own proxy, and read the client address as the entry your outermost trusted hop appended. Accept it from anywhere and a client sets the header themselves, mints a fresh identity per request, and the limiter is decoration.
The second failure is a reverse proxy caching a personalised response: a page carrying a logged-in user's name, returned without Cache-Control: private or a Vary on whatever distinguishes users, is stored under the URL alone and handed to the next user. Cache only what every requester would receive identically.
The forward proxy's single exit IP cuts both ways
The property that makes a forward proxy useful, many clients behind one source address, is also its limit. A partner API rate-limits at 1,000 requests per minute per IP, and 50 machines behind one exit IP share that budget: 1,000 ÷ 50 = 20 per minute each, and only if traffic were even. It never is, so one nightly batch job drains the minute and the other 49 collect 429s. Put a shared limiter in front of the proxy so the fleet draws from one bucket deliberately, or spread egress over a pool of IPs and give up the single address you installed the proxy for.
Where TLS should end
Terminating at the edge is the default: one handshake per client, plaintext on a trusted internal network, cheap backends. Re-encrypting to the backends costs a second handshake per hop, and shared networks and compliance regimes usually require it.
The option to reject out loud is TLS pass-through — a layer 4 proxy forwarding encrypted bytes untouched. It keeps encryption end to end and, because it never sees a request, gives up everything above the transport: no routing on URL, no caching, no compression, no X-Forwarded-For, no per-request retry. Choose it only when the payload must not be readable at the edge.
In an interview
Vocabulary is tested first, consequences second. Almost every design you draw has an intermediary, so name which kind it is and say what changes because it is there. Load balancers, CDN edges and API gateways are all reverse proxies with a specialisation bolted on — the gateway's being authentication, request transformation and per-consumer quotas.
Phrasing that lands: "This is a reverse proxy — clients see one address, TLS ends here, and the app servers have no public route. Load balancing is one job it does from there, not a separate box. Because I terminate at layer 7 the backends lose the client IP, so the rate limiter keys on X-Forwarded-For and only the proxy is trusted to set it."
The mistake that loses points is drawing a forward proxy in your own architecture. It belongs to whoever owns the clients, and in an interview you almost never do — so unless the question is corporate egress, a crawler fleet or an outbound integration, that box is a reverse proxy.
Check yourself
1. Your reverse proxy fronts an origin answering in 200 ms on average, at a peak of 500 QPS. How many origin connections should the pool hold, and what happens at 20?
Little's Law:
500 QPS × 0.2 s = 100requests in flight, so size the pool near 100 with headroom. A 20-connection pool serves 20 and queues the rest — it drains20 ÷ 0.2 s = 100requests per second against 500 arriving, so the backlog grows without bound. Queueing time lands on p99, and the proxy returns 504s while the origin CPU sits idle.
2. App servers rate-limit at 100 requests per minute per client IP. You put a layer 7 reverse proxy in front of them. What is the first bug, and what must ship with the fix?
Every request now arrives from the proxy's address, so the whole user base shares one bucket: roughly the 101st request each minute is rejected regardless of who sent it. Key the limiter on the client address from
X-Forwarded-For, and ship the trust rule with it: honour the header only on connections from your own proxy, then read the list from its right-hand end, discarding one entry per hop you own — the first address left is the client. Read from the left and you take whatever the client typed, because the header is appended to and anyone can pre-populate it.
3. A forward proxy gives 50 machines one exit IP. The partner API allows 1,000 requests per minute per IP. What is the per-machine budget, and what does it rule out?
1,000 ÷ 50 = 20requests per minute per machine, one every three seconds, and only if traffic is perfectly even — it is not. That rules out per-machine limits configured independently: they cannot see each other, so a burst on one starves the rest. Use one shared token bucket in front of the exit IP, or several exit IPs and lose the single address the proxy was installed to provide.