NLB vs ALB: TLS Termination, Passthrough, and mTLS
A common misconception #
“An NLB can’t do TLS, only an ALB can” — this is wrong. Both can terminate TLS. The real distinction is what happens after decryption, and it matters most when a requirement calls for mutual TLS (mTLS).
Where TLS sits, loosely speaking #
TLS doesn’t map cleanly onto a single OSI layer — that mapping is a simplification, not a formal standard. The useful mental model: TLS wraps the TCP stream (L4) and hands a decrypted byte stream up to the application. It performs the handshake and encryption/decryption but does not parse HTTP — HTTP (L7) runs on top of it. “HTTPS” is just HTTP-over-TLS-over-TCP.
L7 HTTP ← application protocol (only ALB reads this)
~L5/6 TLS ← encryption + handshake (loose analogy — NLB can do this)
L4 TCP ← NLB's native layer
L3 IP
Because terminating TLS only requires decrypting at the transport boundary — not understanding HTTP — an L4 device like an NLB can terminate TLS too.
ALB vs. NLB-with-TLS-listener vs. NLB-with-TCP-listener #
| ALB (HTTPS listener) | NLB (TLS listener) | NLB (TCP listener) | |
|---|---|---|---|
| Terminates TLS? | Always — no passthrough mode exists | Yes | No — passes the encrypted stream through untouched |
| Decrypts the byte stream? | Yes | Yes | No |
| Parses HTTP (paths, headers, methods)? | Yes | No | No |
| Routing decisions based on | L7 (host, path, headers) | L4 (port, target group) | L4 (port, target group) |
| WAF support | Yes | No | No |
| mTLS (client-cert validation) support | No (ALB doesn’t support mTLS validation passthrough — termination ends the handshake at the LB) | No — AWS explicitly does not support mTLS on an NLB TLS listener | Yes — only path to mTLS with an NLB, since the handshake reaches the backend |
Both ALB and NLB-with-TLS-listener decrypt the traffic — the difference is what they do with it afterward. ALB reads and routes on the HTTP content underneath; NLB hands off the decrypted TCP stream without ever inspecting it.
Why mTLS forces passthrough #
mTLS requires the backend application itself to receive the client certificate and validate it as part of the initial handshake. If TLS terminates anywhere on a load balancer — ALB or NLB-with-TLS-listener — the handshake ends there, and the application never sees the client cert.
AWS’s NLB documentation is explicit on this: an NLB TLS listener does not support mTLS at all. The only way to get mTLS with an NLB is a TCP listener, where the NLB forwards the raw encrypted bytes untouched and the backend performs the entire TLS handshake — including client-certificate validation — itself.
Key point: the listener port doesn’t decide terminate-vs-passthrough — the listener type does. Port 443 on an NLB behaves completely differently depending on whether it’s configured as a TLS listener (terminates) or a TCP listener (passthrough); the NLB doesn’t even know there’s TLS inside a TCP-listener stream.
Worked example #
Requirement: the application must request a client certificate and validate it as part of the initial handshake (mTLS).
- ALB — terminates TLS always → handshake ends at the LB → app never sees the client cert → fails.
- NLB + TLS listener — terminates TLS by choice, and doesn’t support mTLS anyway → fails.
- NLB + TCP listener — passthrough → handshake reaches the EC2 app → app validates the client cert itself → satisfies the requirement.
Why NLB has no WAF integration #
AWS WAF inspects HTTP-layer content (paths, headers, request bodies), which only an L7 device can provide. Since NLB operates at L4 and never parses HTTP, WAF cannot attach to it directly. Common workarounds: put an NLB in front of an ALB (NLB for static IPs / cross-zone needs, ALB for WAF + L7 routing), or use AWS Shield Advanced for L3/L4 DDoS protection directly on the NLB instead.
Sources: