R1.3 — Gateways: reverse proxies, load balancers and API gateways at the front door

Part of R1. In R1's story, the request met a gateway before any service. Each kind of gateway accepts the client's connection and forwards the request on, so R1.1's picture of connections sits underneath everything here. "Gateway" covers several different jobs, and interviewers notice when they are blurred.

The kinds of gateway

Kind Job Examples
Reverse proxy Hides the servers behind one address; forwards requests HAProxy, Envoy, NGINX
Edge gateway A reverse proxy plus device authentication and protocol translation at the network edge —
Security gateway Firewall rules, DDoS protection, traffic inspection WAF products
Message gateway Validates, transforms (HTTP to JMS, JSON to XML), routes and retries messages Integration platforms in front of a bank's core
API gateway The single entry point for clients into a set of microservices NGINX, Zuul, Spring Cloud Gateway, Apigee, AWS API Gateway

The part people miss about the message gateway: it is not a queue. It transforms and forwards; a queue stores. In payments it is the piece that turns your JSON into the XML a rail expects — R25 covers the message formats.

Reverse proxy, load balancer, API gateway: one shape, three jobs

Three names get used for "the thing in front of the service", and they are easy to blur because physically they are the same kind of box: a server that accepts the client's connection and forwards the request on. What separates them is the job, and how much of the request each one reads to do it.

Three questions tell them apart:

Question Reverse proxy Load balancer API gateway
How many backends does it need? One is enough Two or more copies of the same service Many different services
What does it read? The HTTP request: host, path, headers IP and port (L4), or HTTP (L7) The API: caller identity, plan, quota, version
Does it care who the caller is? No No Yes — that is the point of it

"Something in front of multiple instances of the same service" is the definition of a load balancer. The other two do not need multiple instances at all.

Read them as a ladder of how much each one understands:

Rung Reads Decides Typical products
L4 load balancer Source and destination IP and port; TLS passes through unread Which instance gets this connection AWS NLB, F5 in L4 mode, Kubernetes Service (kube-proxy)
Reverse proxy / L7 load balancer HTTP: host, path, headers, cookies Which backend gets this request; TLS termination, compression, caching, buffering slow clients NGINX, HAProxy, Envoy, Traefik, AWS ALB
API gateway The API consumer: token, API key, client certificate, quota, version Whether this caller may make this call, and which service owns it Kong, Apigee, AWS API Gateway, Spring Cloud Gateway, Zuul

A single NGINX in front of a single Spring Boot instance is a perfectly normal reverse proxy. It terminates TLS, compresses responses, serves static files and holds slow clients so they do not tie up application threads, with no load balancing involved. Add a second instance and one more config block turns it into a load balancer. R14 covers the load balancer's algorithms, health checks and the L4 versus L7 choice in depth.

The trap to name: products overlap, the jobs do not. NGINX and Envoy can play all three roles; ALB does path routing, which is a little of a gateway's job; AWS API Gateway does some balancing. In an interview, name the job first — "I need per-client rate limiting and OAuth validation at the edge" — and the product second.

What an API gateway does

The API gateway takes every concern that would otherwise be copy-pasted into each service and does it once:

These run as an ordered filter chain, and the order is the design: cheap rejections before expensive work.

The gateway filter chain: cheap checks first
flowchart LR
  T["TLS"] --> I["Request ID"]
  I --> W["WAF / size"]
  W --> R["Route match"]
  R --> A["Authn"]
  A --> Z["Authz"]
  Z --> L["Rate limit"]
  L --> X["Transform"]
  X --> U["Upstream call"]
  U --> G["Async log"]

Bad traffic is dropped before the body is parsed; unknown routes get 404 before anyone verifies a token; rate limiting comes after authentication so it knows who to limit. The upstream call is wrapped in timeouts, retries on idempotent calls only, and a circuit breaker (R14). Logging is asynchronous, because the gateway must never block on its own telemetry.

Two design points worth having ready, both covered in full in R17:

Data plane Control plane
Job Process every request Manage, validate and distribute config
Traffic Every request A few changes a minute
State In-memory config snapshot only Database, audit trail
If it fails Outage Nobody can change config; traffic keeps flowing

The control plane can fail; the data plane must not notice. Nodes keep serving on their last good config — the principle called static stability.

What it costs

Trade-off Why it bites
An extra hop A few milliseconds on every request, so the gateway must be non-blocking and keep every lookup in memory
A new critical component If it is down, everything is down — it needs redundancy across zones
Configuration complexity One wrong route or auth rule is a security incident for every service behind it
Cost and lock-in Managed gateways charge per call and shape your architecture
Scope creep Business logic drifts into the gateway; keep complex aggregation in a separate BFF service

An API gateway is not a load balancer.

The gateway decides which service a request belongs to and whether it is allowed in; the load balancer decides which instance of that service takes it. A gateway usually has a load balancer in front of it and balances across upstream instances itself, but neither replaces the other — the stacking below shows how they fit together.

Run a few requests through the pipeline below. Send a normal request and watch it pass authentication, rate limiting, routing and aggregation. Then send one without a token and one in a burst, and notice where each is rejected — a 401 never costs a rate-limit check, and a 429 never reaches a service.

Live · One request through the gateway

Which one, when

Situation Use
One app, one instance, needs TLS, compression, static files A reverse proxy, or let the platform terminate TLS
One service, several instances A load balancer: L7 for HTTP APIs, L4 for raw TCP, very long-lived connections, end-to-end TLS or fixed IP addresses
Several services exposed to external clients or partners An API gateway at the edge, with load balancing per service behind it
Service-to-service calls inside the platform No gateway. Client-side balancing from a service registry, or a service mesh sidecar
Several regions DNS or global routing in front of everything (R14 separates DNS routing from load balancing)

How production systems stack them

The gateway is a service like any other: it runs as several instances across zones, so something has to balance traffic across the gateway itself. That is why real systems stack these layers, each one balancing the layer behind it.

Setting The chain What each layer is doing
One app on a VM Client → NGINX → app Reverse proxy only: TLS, gzip, static files
AWS, simple Route 53 → CloudFront / WAF → ALB → ECS services ALB balances each service's target group and does path routing, standing in for a light gateway
AWS, with API management AWS API Gateway → VPC link → NLB or ALB → services The managed gateway applies keys, auth and throttling; the private integration reaches the services through an internal load balancer
Kubernetes Cloud L4 LB → Ingress controller (NGINX, Envoy) → Service → pods L4 for a stable entry point; the ingress is a reverse proxy doing L7 routing; kube-proxy balances across pods. A gateway product can be the ingress (Kong, Emissary), and mesh sidecars handle east-west traffic
Netflix (the classic published example) AWS ELB → Zuul → services Zuul is the gateway (auth, routing, canaries); it picks instances by client-side balancing from the Eureka registry, so there is no load balancer box behind it
A bank's partner API L4 LB with static IPs → API gateway → internal LB or mesh → payment pods Corporate clients allowlist the static IPs; the gateway checks the client certificate and OAuth token, applies per-client quotas and requires an idempotency key; the internal layer picks a healthy pod

Why an L4 balancer in front of an L7 tier? The L7 tier is many instances, and the L4 layer gives it one stable address, absorbs huge connection counts cheaply, can pass TLS through untouched, and can expose fixed IPs — which a corporate client's firewall team will ask for.

Follow one partner payment through the full stack below. Watch the card underneath: it shows which layers of the request each hop actually reads. The L4 balancer sees only addresses, the gateway is the first to know who is calling, and the internal balancer chooses a pod while skipping one that is draining for a deploy. The last step shows the subsets real systems use when they do not need every layer.

Three mistakes interviewers listen for:

The defensible position: the gateway centralises cross-cutting concerns in a cheap, ordered chain, stays stateless and fast, and never holds business logic. Reverse proxy is the shape; load balancing spreads one service across its copies; the gateway applies API policy across many services. In production they stack — an L4 balancer for a stable entry point, a replicated gateway behind it, and per-service balancing or a mesh behind that.

What to say in the interview

"An L4 load balancer gives the edge a stable address; behind it a replicated gateway does TLS, authentication, rate limiting and routing in a cheap-first filter chain, with the data plane independent of the control plane, and an internal load balancer or mesh picks the instance — the gateway decides which service, the balancer decides which copy."