A platform team picks ALB for everything because the first service they shipped was an HTTPS API and ALB Just Worked. Two years later they have a gRPC service that needs HTTP/2 end-to-end without ALB’s request rewriting, a partner integration that requires a static source IP, and a security audit that wants traffic mirrored through a virtual firewall. They are running three workarounds — a TCP listener on ALB that loses half the L7 features, a NAT gateway tied to a single Elastic IP that breaks every time the underlying instance is replaced, and a manual mirroring rule on every VPC. All three problems are downstream of the wrong load balancer choice at week two. The fix is to know which one to pick for which workload, and to spot the wrong-default before it accretes.
What’s happening
The three load balancer types live at different OSI layers and solve different problems:
-
Application Load Balancer (ALB) — Layer 7. Speaks HTTP/1.1, HTTP/2, gRPC, WebSockets. Routes by host, path, header, query string, source IP, method, and HTTP version. Terminates TLS. Authenticates with Cognito or OIDC. Has WAF integration. Issues per-target health checks against an HTTP path. Logs every request with sub-second granularity. The price is that ALB rewrites the request — it sets
X-Forwarded-For, adds its own headers, and decides which target gets which connection at the HTTP layer, not the TCP layer. -
Network Load Balancer (NLB) — Layer 4. Forwards raw TCP, UDP, or TLS (passthrough). Does not inspect the payload. Can preserve the client source IP end-to-end (no
X-Forwarded-Forbecause there is no HTTP). Supports static IPs — one Elastic IP per Availability Zone, allocated once, never rotated. Handles millions of connections per second with low latency. The price is that NLB does not see HTTP — no path routing, no host header routing, no per-request WAF. Health checks are TCP-level by default; HTTP health checks are possible but the routing decision is still per-connection, not per-request. -
Gateway Load Balancer (GWLB) — Layer 3, sort of. Designed for one job: inserting third-party network appliances (firewalls, IDS/IPS, packet inspection) into the traffic path transparently. It uses GENEVE encapsulation to forward every packet to a fleet of appliances and bring the response back, without the appliance needing a public IP or being inline in the routing table. The appliance vendor markets a “GWLB-ready” image; the customer wires up endpoints and route tables. The price is that GWLB is only useful if you are deploying that kind of appliance. For application traffic between your services, GWLB is not the right tool.
The wrong-default people pick is ALB. It speaks HTTP, it is the one in every tutorial, it gets selected because it is familiar. Then six months in the workload outgrows it — gRPC clients fail because ALB does HTTP/2 to the LB but downgrades to HTTP/1.1 to the target unless configured otherwise; a partner wants to allow-list source IPs and ALB does not have static IPs; a payment processor needs mutual TLS to a specific endpoint and ALB cannot do mTLS passthrough.
The second wrong-default is NLB for a service that is fundamentally HTTP. NLB does not know what a path is. You end up running an nginx layer behind NLB to do path routing, then asking why you bought a load balancer when you have an nginx fleet anyway.
How to verify
The decision matrix, distilled:
| Workload | ALB | NLB | GWLB |
|---|---|---|---|
| HTTPS web app with path-based routing | yes | no | no |
| HTTPS API with WAF and Cognito auth | yes | no | no |
| gRPC service (HTTP/2 end-to-end) | yes (with HTTP/2 target group protocol) | yes (TCP listener) | no |
| WebSocket service | yes | yes | no |
| Partner-facing API needing static source IP | no (use NLB → ALB chain or Global Accelerator) | yes | no |
| Game server with UDP traffic | no | yes | no |
| Database protocol passthrough (MySQL, Redis) | no | yes | no |
| TLS passthrough (client cert validated by app) | no | yes | no |
| Inline firewall or IDS appliance fleet | no | no | yes |
| Email server (SMTP) | no | yes | no |
The single most common pivot we see: a customer-facing HTTPS app that started on ALB and now needs static IPs for a partner integration. Two patterns work:
NLB → ALB chain. NLB is the public-facing layer with Elastic IPs; it forwards to ALB as a target by IP address. ALB does the L7 routing. The partner allow-lists the NLB’s static IPs. The cost is two load balancers in the path, and the source IP visible to ALB is the NLB’s IP, not the client’s (use proxy protocol or X-Forwarded-For from the client side).
Global Accelerator → ALB. Global Accelerator provides two static anycast IPs and forwards to the ALB. The IPs never change, even if the ALB is replaced. This is the simpler pattern when the only requirement is “give me two stable IPs.”
The other pivot worth knowing: gRPC on NLB versus ALB. NLB-as-TCP works for gRPC because NLB does not interfere with the connection — the client and server negotiate HTTP/2 end-to-end. ALB supports gRPC explicitly with the gRPC target group protocol; the routing is per-request, which lets you do canary deployments at the gRPC service level. Both work. ALB wins when you want WAF on the gRPC service or per-method routing. NLB wins when you want the lowest possible latency or when the gRPC service is internal and does not need L7 features.
The fix
The procedure for a new service:
-
Start at L7. If the service speaks HTTP/HTTPS/gRPC and does not have a hard constraint that forces L4, default to ALB. List the L7 features you want — path routing, host routing, WAF, OIDC auth, request logging — and ALB gives you all of them.
-
Promote to L4 (NLB) when one of three constraints lands:
- You need static IPs that survive load balancer replacement (partner allow-lists, on-prem firewall rules).
- You need a non-HTTP protocol (UDP, raw TCP, TLS passthrough for mTLS).
- You need the lowest possible latency for a service that does not benefit from L7 features.
-
Promote to L7 + L4 chain (NLB → ALB) when you need static IPs AND L7 features. This is common enough that it is the default pattern for partner-integrated SaaS.
-
Reach for GWLB only when you are deploying a third-party appliance fleet — a virtual firewall, a packet inspector, a DPI tool. GWLB is not a tool for application traffic between your own services.
-
Document the decision. A service whose ALB choice is “we needed path routing and WAF” is easy to migrate later. A service whose ALB choice is “Terraform module default” is the one that bites in year two.
The other rule we hold: do not switch load balancer type mid-life unless you have a specific failure that the current type cannot address. Each switch is a DNS change, a target-group reconfiguration, and a window of split-brain traffic. The cost of running the wrong load balancer is real but bounded. The cost of running two load balancers because nobody decommissioned the old one is worse.
For more on ALB-specific routing patterns at scale, see ALB listener rules — host and path routing. For the NLB static-IP pattern, see NLB static-IP pattern for partner allow-lists.
Stack Harbor designs ALB + NLB topologies and the operating model around them as part of Managed AWS onboarding and ongoing Managed Cloud Ops. If your load-balancer config has accreted a decade of feature flags, start with an infrastructure consult.