ALB vs NLB vs Classic LB
Elastic Load Balancing (ELB) has three flavours. The Classic Load Balancer is the original (2009) — AWS still supports it but new work should never pick it. The real choice is ALB (L7) vs NLB (L4). Plus a newer fourth option for inspection: GWLB.
Quick verdict
- Default for HTTP/HTTPS apps: ALB
- TCP/UDP, extreme throughput, low latency, static IPs: NLB
- Inserting firewalls/NVAs transparently: GWLB
- Classic: never pick new. Migrate existing to ALB or NLB.
One-line identities
| Type | Layer | Protocols |
|---|---|---|
| Application Load Balancer (ALB) | L7 | HTTP, HTTPS, gRPC, WebSocket |
| Network Load Balancer (NLB) | L4 | TCP, UDP, TLS (pass-through) |
| Gateway Load Balancer (GWLB) | L3 | IP passthrough via GENEVE encapsulation, for transparent NVA insertion |
| Classic Load Balancer (CLB) | L4/L7 hybrid | HTTP, HTTPS, TCP, SSL — legacy, limited |
Side-by-side (ALB vs NLB)
| Aspect | ALB | NLB |
|---|---|---|
| Layer | 7 | 4 |
| Protocols | HTTP/HTTPS/gRPC/WebSocket | TCP/UDP/TLS |
| Routing | Host-, path-, header-, method-, query-string-, source-IP-based | 5-tuple hash |
| Target types | instance, IP, Lambda, ALB-as-target | instance, IP, ALB-as-target |
| TLS termination | Yes (offload) | Yes (TLS listener) or passthrough (TCP listener) |
| Static IPs | No — DNS-only, IPs change | Yes — one static IP per AZ, BYO EIPs supported |
| Preserves client IP | Via X-Forwarded-For header | Yes, natively (for instance targets) |
| WebSocket / long-lived | Yes | Yes |
| HTTP/2 | Yes (front + target) | N/A |
| gRPC | Yes (bidirectional streaming) | Via TCP only |
| Latency (added) | milliseconds | sub-millisecond |
| Throughput | Very high; scales automatically | Millions of requests/second |
| Sticky sessions | Cookie-based (LB-generated or app) | Source-IP affinity |
| Health checks | HTTP/HTTPS with path, codes | TCP / HTTP / HTTPS |
| WAF integration | Yes | No (WAF is L7) |
| Authentication | OIDC, Cognito built-in | No |
| Slow-start | Yes | No |
| Cost model | LCUs (consumption) | NLCUs (consumption) |
ALB — the L7 smart router
ALB makes routing decisions based on request content. This is where it shines:
Listener: HTTPS :443
Rules (evaluated in priority order):
1. host = api.example.com → target group: api-tg
2. path = /images/* → target group: images-tg
3. path = /admin/* AND
header X-Employee = true → target group: admin-tg
4. (default) → target group: web-tg
Target groups hold the actual backends. Targets can be:
- EC2 instances (registered by ID)
- IP addresses (reach any routable IP inside your VPC — includes on-prem via DX/VPN)
- Lambda functions (ALB invokes the Lambda)
- Another ALB (ALB-as-target, for patterns like WAF fronting)
Key ALB-only features:
- TLS offload + SNI — one ALB serves many certificates using SNI, managed via ACM
- ALB authentication — OIDC/Cognito gate before traffic reaches targets
- Sticky sessions — LB-generated cookie or app-defined cookie
- Request/response modifications via listener rules (redirect, fixed response)
- WAF attachable — the L7 inspection layer
NLB — the L4 speed demon
NLB is a pure load balancer at L4 — minimal processing, extreme throughput, very low latency. Uses a flow-hash algorithm on the 5-tuple (src IP, src port, dst IP, dst port, proto) and sticks flows to one target for their lifetime.
Why you’d pick it:
- Fixed IPs — NLB allocates one IP per AZ, can be EIPs. Critical when downstream firewalls allow-list by IP.
- Non-HTTP protocols — game servers (UDP), MQTT, SMTP, custom binary, kubernetes ingress with TCP
- Preserving source IP without header hacks — the target sees the real client IP directly (for instance targets)
- Ultra-low latency — useful in trading, real-time
- Long-running connections — NLB doesn’t close idle connections at the LB layer (outside the ~350s idle timeout for TCP; keepalives help)
NLB doesn’t know HTTP — no URL routing, no X-Forwarded-For injection. If you need both “L4 preserved source IP” and “HTTP path routing,” front an ALB with an NLB (NLB → ALB-as-target).
Cross-zone load balancing
- ALB: always on, free
- NLB: off by default. Without it, each NLB node only balances within its own AZ — uneven distribution if target counts differ per AZ. Enable it for most workloads (small per-GB charge applies).
Classic LB — why it’s deprecated
CLB pre-dates the ALB/NLB split. It blends L4 + light L7 and lacks almost everything modern (no host-based routing, one cert per listener, no target groups, no IP targets, no WebSocket on HTTP listener). The only case for touching one is maintaining an old stack until it can be migrated.
Migration path:
- HTTP/HTTPS CLB → ALB
- TCP CLB → NLB
- Mixed → usually ALB (split the TCP piece to NLB if needed)
GWLB — the newer, specialised option
Gateway Load Balancer is for transparent network-appliance insertion (firewalls, IDS/IPS, DPI). Uses GENEVE (UDP 6081) to tunnel flows to a fleet of virtual appliances, which inspect and hand back. Exposed via GWLB Endpoints inside VPCs — route tables send 0.0.0.0/0 through the GWLB endpoint to force inspection.
Pattern: centralised NVA fleet in an inspection VPC, all spoke VPCs route through it via GWLB + TGW. Replaces the old “bump-in-the-wire” EC2 NVA with clean, scalable insertion.
Target groups — the universal abstraction
Both ALB and NLB (and GWLB) route to target groups rather than directly to instances. One target group can serve multiple listener rules. Multiple load balancers can share target groups? No — one target group, one load balancer.
Important target group attributes:
- Health check — path (for HTTP), interval, threshold, matcher codes
- Deregistration delay — how long to drain before terminating (default 300s)
- Slow start (ALB) — ramp traffic to new targets over N seconds
- Stickiness — session affinity config
Pitfalls
- Picking CLB by habit. Default to ALB or NLB for anything new.
- NLB without cross-zone LB — uneven distribution across AZs when target counts differ.
- ALB preserves client IP only in
X-Forwarded-For— apps must read that header. NLB preserves it at the TCP layer (for instance targets) — different behaviour. - Expecting static IPs from ALB. ALB IPs change. Use Route 53 Alias, or NLB if you must have fixed IPs.
- Security group on NLB (recent feature). NLB didn’t originally support SGs — traffic to instances was filtered only by the instance SG. NLB now supports attached SGs; enable if needed.
- Health-check SG — ALB/NLB health checks come from the LB’s IPs. Instance SG must allow them.
- TLS listener vs TCP listener on NLB — “TLS” terminates; “TCP” passes through (end-to-end TLS to the backend, at the cost of no LB-side cert management).
- gRPC on ALB needs the target group protocol set to
gRPC, and HTTP/2 end-to-end.
Decision flow
Is it HTTP/HTTPS/gRPC?
├── yes → ALB (unless you need static IP → NLB-in-front-of-ALB)
└── no
Is it TCP/UDP?
├── yes → NLB
└── no (IP-level inspection) → GWLB