Load Balancer L4 vs L7#
A load balancer either sees just the TCP/UDP envelope (L4) or also parses the application protocol (L7). The choice drives speed, observability, and routing capability.
flowchart LR
subgraph L4[L4 - transport layer]
C1([Client]) --> NLB[NLB / HAProxy TCP] --> B1[Backend]
NLB -. sees only IP+port .-> Note4[no parse]
end
subgraph L7[L7 - application layer]
C2([Client]) --> ALB[ALB / NGINX / Envoy]
ALB -. parses HTTP .-> Route[route by Host, Path, header]
ALB --> B2[Backend by route]
ALB --> B3[Backend by route]
end
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
class C1,C2 client;
class NLB,ALB edge;
class B1,B2,B3 service;
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
L4 LBs forward at line rate with minimal CPU; L7 LBs add routing flexibility and observability at the cost of CPU and connection handling.
A load balancer's layer determines what it can see, what it can decide, and how fast it can do it.
L4 (transport)#
flowchart TB
C([Client]) --> LB[L4 LB]
LB -.hash on 5-tuple .-> P[pick backend]
P --> B1[Backend 1]
P --> B2[Backend 2]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
L4 LBs forward TCP segments or UDP datagrams without parsing application data:
- Cheap: minimal per-packet CPU.
- Protocol-agnostic: works for HTTP, gRPC, SMTP, MQTT, custom TCP.
- Limited intelligence: cannot route by URL, terminate TLS (without proxy), or inspect headers.
Scheduling algorithms#
| Algorithm | Mechanism |
|---|---|
| Round-robin | Next backend in list |
| Least connections | Backend with fewest active TCP connections |
| Source IP hash | hash(client_ip) → backend; sticky by IP |
| Weighted | Backends with explicit weight |
| Maglev / consistent hash | Minimal churn on backend add/remove |
Direct Server Return (DSR)#
sequenceDiagram
participant C as Client
participant LB as L4 LB
participant B as Backend
C->>LB: request (dst=LB)
LB->>B: forwarded (preserves dst=LB)
B-->>C: reply directly (src=LB - spoofed)
Backend responds directly to client, bypassing the LB on the return path. Doubles aggregate throughput because LB only sees inbound. Used by Google Maglev and high-throughput CDNs. Requires backend kernel cooperation.
L7 (application)#
flowchart LR
C([Client]) --> LB[L7 LB]
LB -.->|Host: api.foo| R1[API fleet]
LB -.->|Host: static.foo| R2[Static CDN]
LB -.->|Path: /admin| R3[Admin fleet]
LB -.->|cookie| SS[sticky backend]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
L7 LBs terminate TCP, parse HTTP (or other protocols), and then make a per-request routing decision:
- Route by Host header: same IP, many tenants.
- Route by URL path:
/api/v1vs/api/v2. - Route by header / cookie: A/B tests, canary, mobile vs web.
- Rewrite URLs / headers: redirects, BFFs.
- Compress, cache, WAF, ratelimit: per-request decisions.
Costs#
- One TCP connection per client AND one per backend (vs L4's pass-through).
- Per-request CPU (parsing, possibly TLS handshake).
- Stateful behind a single LB (connection IDs, request state).
TLS termination#
flowchart LR
C([HTTPS client]) -->|TLS| LB[L7 LB<br/>terminates TLS]
LB -->|HTTP or mTLS| B[Backend]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
LBs typically terminate TLS so they can route on HTTP. To keep traffic encrypted internally, re-originate TLS (or use mTLS) from LB to backend.
Passthrough#
When the LB doesn't need to inspect content (e.g. SNI-only routing), it can passthrough TLS:
- Inspect the TLS ClientHello's SNI extension to pick a backend.
- Forward bytes unmodified.
- Backend terminates TLS.
Sticky sessions#
flowchart LR
C1([Client]) -->|cookie SERVERID=2| LB --> B2[Backend 2]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
L7 LBs implement stickiness via cookies (LB-set or app-set) or hashing. L4 LBs can use source-IP hash. See sticky-sessions.
Combining L4 and L7#
flowchart LR
C([Client]) --> NLB[L4 NLB<br/>line rate]
NLB --> ALB[L7 ALB / Envoy]
ALB --> B1[Backend]
ALB --> B2[Backend]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
Production pattern: an L4 LB at the edge for line-rate ingress + DDoS scrubbing; L7 LBs (Envoy / ALB) downstream for routing. AWS's NLB → ALB chain is canonical.
Health checking#
L4: TCP open succeeds → healthy. Coarse; misses app-level issues.
L7: GET /healthz returns 200 → healthy. More accurate, can verify dependencies.
Active vs passive: active probes (every 5s) catch dead backends quickly; passive (failure on real traffic) catches subtle issues.
Where LB layers fit#
flowchart TB
LL((L4 vs L7<br/>load balancer))
LB[Load balancer<br/>parent topic]
PR[Proxy types<br/>reverse proxy = L7 LB]
SS[Sticky sessions<br/>affinity policies]
GW[API Gateway<br/>specialised L7]
LB --> LL
PR -. same shape .- LL
LL --> SS
LL --> GW
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class LL service;
class LB,PR,SS,GW datastore;
Glossary & fundamentals#
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Load balancer | parent topic | load-balancer |
HLD |
Sticky sessions | session affinity policies | sticky-sessions |
HLD |
Proxy types | reverse proxy = L7 LB | proxy-types |
HLD |
API Gateway | specialized L7 LB | api-gateway |
Quick reference#
L4#
- Sees IP + port only
- Cheap, line-rate
- Protocol-agnostic
- Examples: AWS NLB, HAProxy TCP, IPVS, Maglev
L7#
- Parses HTTP (or other)
- Routes by Host, Path, header, cookie
- TLS termination, compression, WAF
- Examples: AWS ALB, NGINX, Envoy, Traefik
Scheduling#
| Algo | Use |
|---|---|
| Round-robin | Default |
| Least connections | Long-lived connections |
| Source-IP hash | Stickiness without cookies |
| Weighted | Canary / rollout |
| Maglev / consistent hash | Min churn on adds |
DSR (Direct Server Return)#
Backend replies directly to client, bypassing LB on return. Doubles throughput.
TLS modes#
- Terminate at LB (decrypt → route)
- Re-encrypt to backend (mTLS optional)
- Passthrough (SNI-only routing; backend terminates)
Health checks#
- L4: TCP open
- L7: HTTP
/healthz200
Common pattern#
Edge L4 (NLB) → L7 (ALB / Envoy) → backends.
Refs#
- Eisenbud et al. - Maglev (NSDI 2016)
- AWS ELB docs
- Envoy LB overview
- HAProxy docs
FAQ#
What is the difference between L4 and L7 load balancers?#
L4 balances on IP and port without parsing the payload. L7 parses HTTP, gRPC, or other application protocols and routes by host, path, header, or cookie.
When should I use an L4 load balancer?#
Use L4 for raw TCP or UDP traffic, ultra-low latency, end-to-end TLS pass-through, or non-HTTP protocols like databases, gaming, and VoIP.
When should I use an L7 load balancer?#
Use L7 when you need path or host-based routing, header rewrites, A/B testing, sticky sessions by cookie, WAF integration, or per-request observability.
Is L4 faster than L7?#
Yes. L4 forwards packets without parsing application data, so it has lower CPU cost and latency, but it cannot make routing decisions on HTTP headers or URLs.
Can a load balancer be both L4 and L7?#
Yes. Many modern proxies like Envoy, NGINX, and HAProxy support both modes, and cloud providers offer separate L4 NLB and L7 ALB products.
Related Topics#
- Load Balancer: parent topic
- Proxy Types: every reverse proxy is also an L7 LB
- Sticky Sessions: the session affinity story