Load balancer internals — L4 vs L7
Packet routing vs HTTP-aware routing, health checks, sticky sessions.
In 1996, a startup called Alteon Networks shipped the first hardware load balancer that could inspect HTTP request content and route based on the URL path. Before Alteon, load balancing meant DNS round-robin or the first-generation NetScaler box that just forwarded TCP packets based on source-IP hashing. Alteon's trick was to open the TCP payload, parse the HTTP request line, and route /api/* to the API pool while sending /images/* to a CDN origin. The distinction became so fundamental that engineers today still describe load balancers as "Layer 4" or "Layer 7" — using the OSI model as the naming convention.
The distinction is not academic. It determines everything about how your load balancer behaves — what it can route on, what it sees, whether it decrypts TLS, whether it can do canary rollouts, whether it works for non-HTTP protocols like MySQL or Redis, and how it survives DDoS attacks. Confusing L4 with L7 in an interview signals "I know how to configure AWS ALB but haven't debugged a real packet trace." Knowing the difference cold signals systems fluency.
The modern landscape is polymorphic: AWS NLB is pure L4, AWS ALB is pure L7, Envoy and Nginx can operate as either based on configuration, Cloudflare deploys L4 anycast for DDoS scrubbing followed by L7 WAF at the edge PoPs, and Cilium uses eBPF to blur the line entirely by running L4 forwarding decisions in the Linux kernel while making L7 metadata available. Every real system does both. Knowing which layer solves which problem is the goal of this deep-dive.
Prerequisites & cross-references
- OSI Layers concept — this deep-dive assumes familiarity with L4 (Transport) and L7 (Application). Read Network Layers OSI 1-7 first if you need a refresher.
- Load Balancer component — for concrete product picks (ALB vs NLB vs Nginx vs HAProxy), see the Load Balancer component page.
- TLS + HTTP/2 concepts — L7 termination involves TLS handshake + HTTP/2 negotiation. Reference those pages for the underlying primitives.
The core mental model — what each layer SEES
Every packet crossing a load balancer contains layers of headers, wrapped like an onion. Whether the LB peels each layer or forwards the packet as an opaque blob defines its class.
┌─────────────────────────────────────────────┐ │ L2 Ethernet header (MAC addresses) │ ← visible ├─────────────────────────────────────────────┤ │ L3 IP header (src IP, dst IP, TTL) │ ← visible ├─────────────────────────────────────────────┤ │ L4 TCP header (src port, dst port, flags) │ ← visible ← ROUTES HERE ├─────────────────────────────────────────────┤ │ ▓▓▓▓ ENCRYPTED TCP PAYLOAD ▓▓▓▓ │ ← opaque │ ▓▓▓▓ (never opened by L4 LB) ▓▓▓▓ │ └─────────────────────────────────────────────┘ Routing decisions based on: destination port, source IP hash, TCP flags, connection state. L4 has NO IDEA what protocol runs inside.
The question every candidate asks — "If L4 doesn't know it's HTTP, how does it route?"
This is the most common source of confusion. L4 has no idea what "GET" or "/api" is. So how does an L4 load balancer make routing decisions? Answer: it uses only what's in the TCP + IP headers — and that's enough for most workloads.
- Destination port — packets on port 443 → HTTPS pool; port 3306 → MySQL pool; port 6379 → Redis pool. One LB, many backend pools.
- Source IP — hash-mod-N to a backend, giving sticky sessions. Same client → same server (until the pool changes).
- TCP flags — SYN means a new connection: pick a backend. Existing connection: forward to the same backend (via connection tracking table).
- Round-robin or least-connections — no client state at all. Just pick the least-loaded backend for each new SYN.
The L4 LB opens the SYN packet, decides on a backend, records "this source-IP:src-port ↔ backend" in its connection table, then blindly forwards every subsequent packet on that TCP connection. It never looks at the TCP payload. This makes L4 forwarding fast (kernel-level, ~5μs per packet) and protocol-agnostic (works for SSH, MySQL, gRPC, WebSockets, custom TCP — anything).
Eight scenarios — pick a workload, see which layer wins
The theory is clear. The practical test is: given a specific workload, which layer does the routing? Click through these to see how L4 or L7 handles each:
LB never terminates TLS. Backend does. LB just forwards encrypted bytes based on destination port + connection state.
What L4 can do that L7 cannot
Non-HTTP protocols
MySQL, PostgreSQL, Redis, MongoDB, SSH, RDP, custom TCP services, MQTT, gRPC with mTLS passthrough. Any protocol over TCP works transparently because L4 never inspects the payload.
Sub-microsecond forwarding
Kernel-level via IPVS, DPDK, or XDP. Millions of packets per second on commodity hardware. Google Maglev routes hundreds of gigabits per second per node.
Extreme PPS + DDoS resistance
SYN flood filtering, source-IP rate limits, sub-second packet drops for known-bad IPs. Because L4 sees only headers, it can reject at line rate without allocating memory per connection.
Transparent / DSR mode
Direct Server Return: LB forwards the request, backend responds directly to the client without going back through the LB. Halves LB bandwidth. Only works at L4 because L7 needs the response stream to inject headers or cache.
Encrypted passthrough
TLS traffic never decrypted by the LB. Backend keeps the key. Reduces LB attack surface, simplifies key management, satisfies compliance regimes that forbid intermediate decryption.
UDP + QUIC + DNS
UDP-based protocols (HTTP/3 QUIC, DNS, VoIP, gaming, VPN) require L4 forwarding — L7 concepts don't apply until a session layer is negotiated. L4 forwards the datagram to the right backend based on 4-tuple.
What L7 can do that L4 cannot
Path-based routing
/api/* → API pool, /static/* → CDN, /admin/* → admin pool. Impossible at L4 because paths live inside the HTTP request line, which L4 never reads.
Header / cookie routing
Canary rollouts (5% of traffic with X-Canary: true), A/B tests, mobile-vs-web splits, API-tier routing based on X-API-Version. All impossible at L4.
Request / response rewriting
URL rewriting, header injection (add X-Forwarded-For), response compression (gzip / brotli), body modification (WAF), inject tracing headers, cache invalidation.
Application-aware health checks
Not just "TCP handshake succeeded" but GET /health returned 200 with body OK. Catches "service running but broken" states that L4 health checks miss entirely.
WAF + rate limiting per API key
SQL injection blocking, XSS pattern rejection, bot detection via User-Agent, rate limits scoped to authenticated user rather than source IP. Requires reading + understanding HTTP.
HTTP/2 + HTTP/3 termination + SNI
Multiplexing many logical streams over one TCP connection. Server Name Indication routes different hostnames to different backends over a single TLS endpoint. Both require decrypting TLS at the LB.
Product landscape — who does what
| Product | Layer | Protocols | Strength | Weakness |
|---|---|---|---|---|
| AWS NLB | L4 | TCP/UDP/TLS-passthrough | Millions PPS, cheap, static IP, DDoS resistant | No HTTP awareness. No canary, no WAF, no rewriting. |
| AWS ALB | L7 | HTTP/HTTPS/gRPC/HTTP/2 | Path routing, host routing, WAF integration, canary via weighted target groups | HTTP only. More expensive than NLB. Higher latency (~5ms overhead). |
| Nginx | L4 or L7 | TCP/HTTP/HTTPS | Free, ubiquitous, mature, can operate as either layer with config change | Config file complexity. Requires manual scaling. |
| HAProxy | L4 or L7 | TCP/HTTP | Battle-tested performance, sticky sessions primitive, health checks | Config verbose. Learning curve. |
| Envoy | L4 or L7 | HTTP/1.1/2/3, gRPC, TCP | Modern, dynamic reconfiguration via xDS, service-mesh native | YAML/JSON control plane complexity. |
| IPVS / LVS | L4 | TCP/UDP | Kernel-native forwarding, sub-microsecond, Linux stock | L4 only. Config is arcane. Kubernetes abstracts it away. |
| Google Maglev | L4 | TCP/UDP | Multi-Tbps per site, consistent hashing, DPDK-based | Google internal. Concepts documented in SIGCOMM 2016. |
| F5 BIG-IP LTM | L4 + L7 | Everything | Enterprise features, TCL scripting for arbitrary logic, hardware appliance | Expensive licensing. Vendor lock-in. |
| Cloudflare / Fastly | L4 + L7 | HTTP + anycast + WAF | L4 anycast + L7 WAF + edge compute in one service | Multi-tenant, so per-customer feature limits. |
| Cilium (eBPF) | L4 with L7 metadata | TCP/UDP with HTTP awareness | L4 speed + L7 policy via kernel programmability, native K8s CNI | Recent tech, kernel version requirements, learning curve. |
Modern hybrid — Cloudflare + Cilium blur the line
Real-world large-scale deployments almost always run both layers in series. The current gold-standard pattern:
- L4 anycast at the edge — Cloudflare, Google Cloud, AWS Global Accelerator announce the same IP from hundreds of PoPs. BGP routes each client to their nearest PoP. First-line DDoS scrubbing here (SYN flood filtering, source-IP rate limits) drops 99% of malicious traffic before it enters the LB fleet.
- L7 at the PoP or origin — TLS terminated, HTTP parsed, WAF applied, path-based routing to correct origin, cache lookup, rate limits per API key. This is where feature intelligence lives.
- Backend service mesh — Istio, Linkerd, or Cilium sidecar-inject L7 proxies next to every service pod. mTLS terminates inside the mesh. Fine-grained per-service policy at the pod level.
Cilium's eBPF approach is the interesting outlier: because eBPF programs run in the Linux kernel, Cilium can make L4 forwarding decisions with L7 metadata as context without paying the userspace-copy cost. It's effectively "L4 speed with L7 awareness" — a genuinely new point on the trade-off curve enabled by kernel programmability.
Real-world deployments
L7 Zuul for API routing
Zuul is Netflix's L7 API gateway — filters route based on path, header, and body content. Handles >100B requests/day. Written in Java (Netty). Used for canary rollouts, resilience patterns (Hystrix), authentication, per-device routing.
L4 NLB for gRPC bulk data
LinkedIn's internal RPC uses gRPC over long-lived TCP connections for bulk data transfer. AWS NLB is used because gRPC multiplexes many streams over one connection — L7 buffering would introduce latency. L4 forwarding gives them 90-99% throughput vs unbuffered TCP.
L4 anycast for WebSocket stickiness
Discord's voice + text messaging uses WebSocket over TLS. They use L4 anycast (Cloudflare) to route each client to the nearest PoP, then L7 sticky routing within the PoP to keep sessions on the same backend. Combines L4 + L7 to solve DDoS + session affinity together.
L4 scrubbing + L7 WAF
Every request through Cloudflare is inspected by their L4 scrubbing layer first (SYN flood, amplification attacks, spoofing). Traffic that survives is passed to their L7 WAF (SQLi patterns, bot detection) at the edge PoP. Two-tier defense means most attacks never even reach L7.
Service (L4) vs Ingress (L7)
A Kubernetes Service of type ClusterIP is pure L4 — iptables (or IPVS) load balances TCP/UDP to pod IPs. An Ingress resource is L7 — path-based routing terminated by Nginx / Traefik / Envoy. Both exist because both problems are real.
Purpose-built L4 anycast
Google's Maglev is a software L4 LB running on commodity servers. Uses consistent hashing for connection stickiness, DPDK for kernel-bypass forwarding. Powers Google Search, Gmail, YouTube at multi-Tbps per site. SIGCOMM 2016 paper.
Interview cheat sheet
Q: Should I use L4 or L7 for this workload?
A: Answer both parts:
- "L4 for anything non-HTTP: databases, caches, custom TCP, WebSockets with encrypted passthrough, UDP-based protocols."
- "L7 for HTTP workloads where routing depends on request content: path, header, cookie, body. Canary rollouts, A/B tests, WAF, HTTP-level rate limiting, request rewriting."
- "At production scale, both: L4 for first-line DDoS scrubbing at anycast edges, L7 at PoPs or behind the L4 tier for feature-rich routing. Cloudflare + Cloud provider LBs + service mesh all combine layers."
Follow-up probe (the interviewer will usually ask):
- "Why can't L4 do canary rollouts? Because canary rules match on headers or cookies, which live in the HTTP request. L4 never opens the TCP payload; it has no visibility into headers."
- "Why is L4 faster than L7? L4 makes a routing decision per connection (once at SYN); L7 can re-route per request (many times per connection). L7 has to buffer, parse, decrypt, re-encrypt, and re-forward every request — that's dozens of microseconds vs L4's single-digit microseconds."
- "How does L4 handle sticky sessions? Source-IP hashing: hash(source_ip) mod N picks the backend. Works until the client is behind a NAT / CGNAT (many clients share IP) or the pool changes. Better for stateful UDP; worse for shared corporate networks."
Key takeaways
- L4 = connection-level routing. Uses only IP + TCP headers. Protocol-agnostic. Fast (kernel-speed). Blind to HTTP content.
- L7 = request-level routing. Parses HTTP. Can inspect path, headers, cookies, body. Slower per request (userspace + buffering) but massively richer feature set.
- L4 answers "where should this connection go?" ONCE per connection. L7 answers "where should this request go?" per request — many per connection with HTTP/2.
- Modern deployments run BOTH. L4 at anycast edges for DDoS scrubbing, L7 at PoP or origin for feature routing. Cilium blurs the line via eBPF.
- Choose L4 for: non-HTTP protocols, encrypted passthrough, extreme PPS, DDoS scrubbing, UDP + QUIC.
- Choose L7 for: path routing, canary rollouts, WAF, HTTP compression, header manipulation, application health checks, HTTP/2+HTTP/3 termination.
Practice what you just read
Every foundation concept has a companion quiz to close the loop.