Load Balancing: Layer 4 vs Layer 7
A load balancer spreads connections so one machine does not take all the pain. Layer 4 routes on IP and port — fast and protocol-agnostic. Layer 7 reads HTTP hosts, paths, and headers — smarter routing, more work per request. The horizontal vs vertical scaling guide explains when adding balanced instances is the correct capacity move.
Wrong layer means either blind TCP fan-out when you needed /api vs /static, or HTTP parsing overhead when you only needed raw throughput. This article compares both, shows a request path with health-check failure, and gives a decision rule you can defend with evidence. For a concrete L7 configuration, continue with the NGINX gateway guide.
Layer 4 (Transport) Load Balancing
An L4 balancer sees source/destination IP and port — not HTTP paths. A client connects to :443; the balancer picks a backend (round-robin, least connections, or IP hash) and forwards the TCP (or UDP) stream. It does not terminate TLS or read cookies unless you add a separate terminator.
Use L4 for non-HTTP workloads (databases, many gRPC/WebSocket setups) or when you want minimal per-connection work at the edge. AWS Network Load Balancer, HAProxy in TCP mode, and Linux IPVS operate here. Pass the real client IP with PROXY protocol when backends need it.
| Layer | Route using | Use when | Trade-off |
|---|---|---|---|
| L4 | IP, port, connection state | Raw TCP/UDP or protocol-agnostic fan-out | Cannot route by HTTP host, path, or header |
| L7 | HTTP host, path, header, cookie | TLS termination and content-aware routing | Parses and operates on application traffic |
Quick reference
- Best for: TCP/UDP, databases, protocol-agnostic fan-out, high connection counts.
- Strengths: low per-packet work; any protocol on the port.
- Weaknesses: no URL routing; no content-based decisions.
- Algorithms: round-robin, least connections, IP hash (IP sticky).
- Use PROXY protocol when backends must see the original client address.
- When not to use alone: you need path/host routing or cookie affinity on HTTP.
Remember this
Choose L4 when protocol flexibility and low overhead matter more than HTTP awareness.
Layer 7 (Application) Load Balancing
An L7 balancer understands HTTP(S). It can terminate TLS, read Host, path, cookies, and headers, then route /api/* to the API pool and / to the web pool. That enables host-based vhosts, sticky cookies, rewrites, and WAF hooks. AWS Application Load Balancer, NGINX, and Traefik are common L7 choices.
Request trace: Client → L7 (TLS) → match Host + path → select upstream → proxy → response. In open-source NGINX, max_fails and fail_timeout implement passive checks based on failures from real proxied requests; they do not periodically probe /health. Active HTTP probes require NGINX Plus or another health-checking component.
Quick reference
- Best for: HTTP APIs, microservices path routing, TLS termination at the edge.
- Strengths: path/host routing and header rewrites; health-check behavior depends on the product/edition.
- Open-source NGINX max_fails is passive; periodic active probes are an NGINX Plus feature.
- Weaknesses: more CPU per request than L4; HTTP-centric by default.
- Route by path or host; use cookie stickiness only when the app is not yet stateless.
- Do not load-balance public static assets a CDN can cache.
Remember this
Choose L7 when HTTP routing, TLS termination, and content-aware decisions are required.
Combining L4 and L7 in Production
Many production edges stack both: L4 (or DNS/anycast) at the outer perimeter for connection distribution and DDoS absorption; L7 inside for TLS, path routing, and service dispatch. In Kubernetes a common shape is Internet → cloud LB → Ingress (L7) → Service → Pod. Pure TCP/gRPC or database traffic may stay on L4 services end to end.
Decision evidence: If you only need “spread TCP to N identical nodes,” L4 is enough. If product routing depends on URL, headers, or hostnames, you need L7 somewhere. Measure p95 latency and error rate before and after moving TLS termination — do not assume L7 is “always slower enough to matter” without your workload.
Quick reference
- Edge: L4 for high-connection entry and simple TCP fan-out.
- Application: L7 for SSL, path routing, microservice dispatch.
- Internal: L4 often fine for service-to-service gRPC or DB pools.
- Health checks: configure the actual balancer/edition to call an HTTP endpoint that reflects dependencies.
- Attach autoscaling target groups so new instances join automatically.
- Multi-region: GeoDNS or anycast entry, then local L7.
Remember this
Stack L4 at the edge for connections and L7 inside for HTTP intelligence — pick by routing need, not fashion.
Recover One Health-Check Failure End to End
Trigger. db1 behind the HAProxy TCP pool above stops accepting connections — a crash, an OOM kill, a bad deploy. Symptom. For the next few seconds, some client connections still route to db1 and hang or reset, because HAProxy hasn't yet proven the backend is actually down; a health check is a sampled signal, not an instantaneous one.
Root mechanism. The config's fall 2 requires two consecutive failed checks at inter 2s before HAProxy marks a server DOWN — a deliberate debounce against a single dropped packet flapping a healthy server in and out of rotation, at the direct cost of up to ~4 seconds where new connections can still land on a backend that's actually dead. Recovery: once db1 fails its second check, HAProxy removes it from the pool and every new connection routes to db2 only — no code change, no manual intervention, the mechanism the config already declared. Verification: run repeated nc -zv connection attempts through the failure window and confirm they shift from intermittent failures to consistently succeeding against db2 alone once the fall threshold is crossed. Prevention: tune fall/rise/inter against your actual failure tolerance — a lower fall detects failure faster but risks flapping a backend with occasional transient blips; verify the trade-off against a real failure injection, not just the default values from a tutorial.
Quick reference
- Trigger: a backend stops accepting connections without warning.
- Symptom: some new connections still land on the dead backend until the check threshold is crossed.
- Root mechanism: fall 2 at inter 2s is a deliberate debounce — real but bounded detection delay, not a bug.
- Recovery: HAProxy's own mechanism removes the backend from rotation with no manual step required.
- Prevention: tune fall/rise/inter against a real failure injection test, not default values assumed to be correct.
Remember this
A health check's fall/rise thresholds are a deliberate trade between fast failure detection and flapping-avoidance — the bounded detection delay during that trigger is expected behavior, not a defect to eliminate to zero.
Key takeaway
L4 optimizes for transport distribution; L7 adds HTTP-aware control. Systems may use either or both at different boundaries. Defend the choice with required routing behavior and measured latency/error impact, not vendor throughput claims.
Practice (20 min): run two local app instances with the /health endpoint above and point the shown open-source NGINX upstream at both. Stop one instance, send repeated curl --fail http://localhost/api/health requests, and observe passive failures plus sibling responses in the access/error logs. If you need removal without client traffic, configure an active-check product or orchestrator and state that prerequisite explicitly.
Related Articles
Explore this topic