L4 vs L7 load balancing
“What is the difference between a layer-4 and a layer-7 load balancer? What does each one see, what can each one do, and what does each one break?”
What this tests
- What "sees" means: headers available at each layer
- Connection-level vs request-level distribution and its consequences
- What terminating TCP and TLS buys and costs
- Awareness that L7 proxies introduce their own timeouts, errors and identity
Answers by level
Read the beginner answer first and notice what is missing.
An L4 balancer sees the IP and TCP/UDP headers — the five-tuple — and nothing inside the payload. It comes in two flavours: packet-forwarding (IPVS/LVS, Google Maglev, AWS NLB, Kubernetes kube-proxy) which rewrites addresses or uses direct server return and never holds a TCP connection itself, so it moves millions of packets per second and preserves the client IP; and a TCP proxy (HAProxy mode tcp, nginx stream) which terminates the client’s connection and opens another to the backend. Either way it distributes connections: every byte of a connection goes to the backend chosen at SYN time. It cannot route by path or host, cannot retry a request, cannot see whether the backend answered 500, and its health check is "the port accepts a connection", which is not the same as "the service works". It also cannot see TLS, which is a feature when end-to-end encryption or mTLS to the backend is required. See Load Balancers: L4 vs L7.
An L7 balancer terminates TCP *and* usually TLS, parses HTTP, and distributes requests: each request on a client connection can go to a different backend over a pool of keep-alive connections. That unlocks routing on host, path, header and cookie; canaries and weighted splits; per-request retries with a budget; timeouts, rate limits and circuit breaking; header injection (X-Forwarded-For, X-Forwarded-Proto, request ids); HTTP/2 or HTTP/3 toward clients with HTTP/1.1 toward backends; compression, caching, WAF. Its health check can be a real GET /healthz. The price is CPU (TLS + parsing), a little latency, and a new identity in the middle: the backend sees the balancer’s IP, so client identity must travel in a header the backend has to *trust* only from that hop; the balancer becomes the one that generates 502/503/504 and enforces its own idle and request timeouts; request or response buffering silently breaks streaming bodies and SSE; WebSocket needs explicit upgrade support. See Forward and Reverse Proxies.
The trade-off in one line: L4 is transparent and cheap but distributes the wrong unit for HTTP, L7 distributes the right unit and can fix things but is itself a component that fails. Connection-level distribution matters more than it sounds — with keep-alive or HTTP/2, one client connection may carry thousands of requests, so an L4 balancer in front of a few chatty clients produces badly uneven backend load, and a freshly added backend gets nothing until connections churn. Most real deployments stack them: an L4 tier for raw ingress and DDoS absorption, an L7 tier (nginx, Envoy, ALB, an ingress controller) behind it for the HTTP intelligence. Non-HTTP protocols — databases, MQTT, raw gRPC-over-TLS with mTLS to the pod — use L4 by necessity.
Green flags · Red flags
X-Forwarded-For trust rule.- Distinguishes what each layer can read: five-tuple vs HTTP headers
- Says L4 distributes connections and L7 distributes requests, and gives a consequence
- Knows L4 preserves end-to-end TLS and client IP; L7 terminates and must forward identity
- Names L7 side effects: buffering, its own timeouts and 5xx codes, WebSocket upgrade
- Describes a stacked deployment and why
- Reduces it to "L4 is fast, L7 is smart"
- Thinks an L4 balancer can route on URL
- Unaware the backend sees the balancer’s IP
- Cannot say what a TCP health check misses
Follow-up questions
X-Forwarded-For is being bypassed. What is wrong?/healthz but every real request 500s. What kind of health check would have caught it?