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

TypeLayerProtocols
Application Load Balancer (ALB)L7HTTP, HTTPS, gRPC, WebSocket
Network Load Balancer (NLB)L4TCP, UDP, TLS (pass-through)
Gateway Load Balancer (GWLB)L3IP passthrough via GENEVE encapsulation, for transparent NVA insertion
Classic Load Balancer (CLB)L4/L7 hybridHTTP, HTTPS, TCP, SSL — legacy, limited

Side-by-side (ALB vs NLB)

AspectALBNLB
Layer74
ProtocolsHTTP/HTTPS/gRPC/WebSocketTCP/UDP/TLS
RoutingHost-, path-, header-, method-, query-string-, source-IP-based5-tuple hash
Target typesinstance, IP, Lambda, ALB-as-targetinstance, IP, ALB-as-target
TLS terminationYes (offload)Yes (TLS listener) or passthrough (TCP listener)
Static IPsNo — DNS-only, IPs changeYes — one static IP per AZ, BYO EIPs supported
Preserves client IPVia X-Forwarded-For headerYes, natively (for instance targets)
WebSocket / long-livedYesYes
HTTP/2Yes (front + target)N/A
gRPCYes (bidirectional streaming)Via TCP only
Latency (added)millisecondssub-millisecond
ThroughputVery high; scales automaticallyMillions of requests/second
Sticky sessionsCookie-based (LB-generated or app)Source-IP affinity
Health checksHTTP/HTTPS with path, codesTCP / HTTP / HTTPS
WAF integrationYesNo (WAF is L7)
AuthenticationOIDC, Cognito built-inNo
Slow-startYesNo
Cost modelLCUs (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

  1. Picking CLB by habit. Default to ALB or NLB for anything new.
  2. NLB without cross-zone LB — uneven distribution across AZs when target counts differ.
  3. 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.
  4. Expecting static IPs from ALB. ALB IPs change. Use Route 53 Alias, or NLB if you must have fixed IPs.
  5. 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.
  6. Health-check SG — ALB/NLB health checks come from the LB’s IPs. Instance SG must allow them.
  7. 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).
  8. 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

See also