Proxies, load balancers and CDNs

Forward versus reverse proxies, layer 4 versus layer 7 balancing, health checks and sticky sessions, where TLS terminates, and how CDN caching rules actually behave.

Who the proxy is serving

KindServesTypical useSees the request
Forward proxyThe clientEgress control, caching, anonymityFull URL and headers
Reverse proxyThe serverTLS termination, routing, compressionFull request after decryption
L4 load balancerEitherRaw TCP or UDP distributionOnly addresses and ports
L7 load balancerThe serverRouting on path and headersFull HTTP request
API gatewayThe serverAuth, rate limits, routingFull request plus policy
CDN edgeThe serverStatic and cacheable contentFull request at the edge
# headers that let the origin see the real client
X-Forwarded-For: 203.0.113.10, 198.51.100.7
X-Forwarded-Proto: https
X-Forwarded-Host: api.example.com
Forwarded: for=203.0.113.10;proto=https;host=api.example.com

# and the hop-by-hop headers a proxy must not forward
Connection: keep-alive
Transfer-Encoding: chunked

Trust only the forwarded headers that come from your own edge. A client can send X-Forwarded-For itself, so an origin that reads the leftmost value without validating the source is trivial to spoof.

Choosing a balancing strategy

  • Round robin spreads evenly but ignores load and connection cost.
  • Least connections handles uneven request durations better.
  • Consistent hashing keeps a key on the same backend, which helps caches and stateful sessions.
  • Least response time reacts to latency but can oscillate under changing load.
  • Random with two choices is simple and close to optimal at large scale.
health check design

passive   observe real traffic; eject after N failures
active    probe a dedicated endpoint on a fixed interval

a good probe checks:
  the process is accepting connections
  a dependency it needs is reachable
  the response is a known body, not any 200

do not probe:
  the full user-facing path (too slow, too flaky)
  a shared dependency with a short timeout in a way
  that lets one slow dependency eject every node at once
DecisionOption AOption BChoose B when
TLSTerminate at the edgePass through to the backendCompliance requires end-to-end encryption
SessionsSticky sessionsShared session storeYou want to scale and deploy without draining
TimeoutsShort and aggressiveLong and lenientRequests are legitimately slow and retries are safe
RetriesRetry at the proxyRetry in the clientThe client knows which operations are idempotent

What a CDN caches

Cache-Control: public, max-age=31536000, immutable     # hashed static asset
Cache-Control: public, s-maxage=300, stale-while-revalidate=60
Cache-Control: private, no-store                         # per-user response
Cache-Control: no-cache                                  # revalidate every time
Vary: Accept-Encoding, Accept-Language                   # separate cache entries
  • max-age applies to browsers; s-maxage overrides it for shared caches such as a CDN.
  • stale-while-revalidate serves a stale copy while fetching a fresh one, which removes the latency spike at expiry.
  • A Vary header multiplies cache entries; a careless one such as Vary: User-Agent destroys the hit rate.
  • Only cache a response per user if the cache key includes the user identity, and usually it should not be cached at all.
  • Purge by tag or by surrogate key so a content update invalidates every related variant, not just one URL.
⚠️
A shared cache and a Set-Cookie response are a dangerous combination. If a per-user response is cacheable by the edge, the next visitor can receive the previous visitor's session. Mark authenticated responses private and no-store unless you have designed the cache key carefully.

FAQ

Should the load balancer retry failed requests?
Only idempotent ones, and only when the failure happened before the request was processed. A blind retry on a non-idempotent POST can duplicate the effect.
Why is my CDN hit rate low?
Usually query strings or varying headers. Normalise or ignore irrelevant query parameters, and audit every Vary header — each one splits the cache.

DNS deep dive: zones, records and DNSSEC Network debugging toolkit

Last refreshed 2026-09-18.