ALB vs NLB (and When to Use Each)
Introduction¶
AWS has three current load balancer types: ALB (Application, L7), NLB (Network, L4), and GWLB (Gateway, for inline appliances — out of scope here). For a normal web service the choice is ALB vs NLB, and it comes down to whether you need HTTP-aware routing or raw L4 pass-through.
Feature comparison¶
| ALB (L7) | NLB (L4) | |
|---|---|---|
| OSI layer | 7 (HTTP/HTTPS/gRPC/WebSocket) | 4 (TCP/UDP/TLS) |
| Path / host / header routing | yes | no |
| Redirects, fixed responses | yes | no |
| TLS termination | yes | yes (TLS listener) — or pass-through TCP |
| Source client IP to target | via X-Forwarded-For header |
preserved (real IP) with target-type instance/ip |
| Static IP / Elastic IP | no (DNS name, IPs change) | yes (one per AZ, can assign EIPs) |
| Extreme throughput / low latency / millions of connections | good | better (kernel-level, no per-request processing) |
| WebSocket / long-lived connections | yes | yes |
| Idle timeout | configurable (default 60s) | 350s for TCP, not configurable for TCP flows |
| Authentication (OIDC/Cognito) | yes | no |
| WAF integration | yes | no (put a WAF elsewhere) |
| Slow start, least-outstanding-requests | yes | no (flow hash) |
| Health checks | HTTP/HTTPS with expected codes/body | TCP, HTTP, HTTPS |
| Pricing | per hour + LCU (new conns, active conns, bandwidth, rule evals) | per hour + NLCU (similar, generally cheaper for high bandwidth / low request rate) |
Choose ALB when¶
- It's HTTP(S) and you want path/host routing (one LB, many services / microservices, an ingress).
- You want TLS termination + ACM, HTTP→HTTPS redirect, HSTS at the edge.
- You want WAF, OIDC/Cognito auth at the load balancer.
- gRPC or WebSocket apps that benefit from L7 features.
- Kubernetes Ingress on EKS (the AWS Load Balancer Controller provisions ALBs from Ingress).
Choose NLB when¶
- You need a static IP (firewall allowlisting by the client, or a fixed IP for a partner integration).
- Non-HTTP protocols — a database proxy, a game server, SMTP, MQTT, DNS (UDP), syslog.
- Extreme scale — millions of concurrent connections, very high PPS, or latency-critical (NLB adds ~microseconds; ALB adds ~milliseconds).
- TLS pass-through — the backend must terminate TLS itself (mTLS to the app, or the app needs the raw cert).
- You want the real client IP with zero application changes (no
X-Forwarded-Forparsing). - Kubernetes
Service type: LoadBalancerfor a non-HTTP service.
Common patterns¶
- ALB behind an NLB — NLB for a static IP +
X-Forwarded-For-free client IP, ALB behind it for L7 routing. (AWS supports ALB as an NLB target.) - CloudFront → ALB — CDN + edge caching in front, ALB for origin routing.
- NLB for ingress, per-service ALBs internally — rare; usually one ingress ALB is enough.
Verification and troubleshooting¶
aws elbv2 describe-load-balancers --query 'LoadBalancers[].{Name:LoadBalancerName,Type:Type,Scheme:Scheme,DNS:DNSName}'
aws elbv2 describe-target-health --target-group-arn ...
aws elbv2 describe-target-groups --query 'TargetGroups[].{Name:TargetGroupName,Proto:Protocol,HC:HealthCheckPath}'
dig +short my-alb-123.eu-west-1.elb.amazonaws.com # ALB IPs change — always use the DNS name
- Targets unhealthy on an ALB — health check path returns non-2xx, wrong port, or the target's security group doesn't allow the ALB's SG.
- NLB targets unhealthy — for
target-type: instance, the health check goes to the instance on the target port and the instance SG must allow the NLB's subnet CIDRs (NLB has no security group of its own pre-2023; newer NLBs can have one). Foriptarget-type +preserve_client_ip, the target SG must allow the client IPs, not the NLB's. - App sees the LB's IP, not the client's — ALB: parse
X-Forwarded-For(rightmost trusted entry). NLB instance/ip: client IP is preserved at L4; if the app still sees a private IP,preserve_client_ipis off or it's analb-type target. - Connections dropped at ~350s (NLB) — the TCP idle timeout; send keepalives or app-level pings for long-lived connections. ALB's 60s default is even shorter — raise it.
- 502 from ALB — backend closed the connection, response headers malformed, or keep-alive timeout on the target shorter than the ALB's idle timeout (set the target's keep-alive higher than 60s).
- Uneven load across AZs (NLB) — cross-zone load balancing is off by default for NLB (and incurs cross-AZ data charges). Enable it if you want even distribution.
- Static IP "changed" — you used the ALB DNS name and resolved it once. ALB IPs rotate; use the DNS name. NLB IPs are stable (one per AZ).
Related tools and reading¶
- On-site: HTTP Header Analyzer, Port Checker, SSL Certificate Checker.
- Related posts: The AWS Load Balancer Controller, Route 53 routing policies.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Cloud & AWS
AWS WAF: The Rules That Matter
AWS WAF filters HTTP requests at the ALB/CloudFront/API Gateway edge. Here's a sensible baseline Web ACL, how to tune with count mode, and the false-positive traps.
October 24, 2026An S3 Static Site Behind CloudFront
The right way to host a static site on AWS: a private S3 bucket, CloudFront with OAC in front, HTTPS via ACM, and SPA fallback routing. Plus the 403s that trip everyone.
October 24, 2026CloudWatch Logs and Metric Filters
CloudWatch Logs is cheap to write and expensive to forget about. Here's retention, turning error patterns into metrics and alarms, and Logs Insights for ad-hoc queries.
October 24, 2026