What it means
Every ALB response that the load balancer generated itself carries Server: awselb/2.0. For a 502, AWS lists these causes (as of October 2026): the target reset the connection, closed it while a request was outstanding, sent a malformed response or a response header over 32 KB, failed the TLS handshake, or was deregistered before the request finished. Confirm who answered:
curl -sI https://example.com/api/health | grep -iE "^HTTP|^server"Common causes
1. Keep-alive shorter than the idle timeout. The ALB reuses idle connections for up to 60 seconds. Node.js closes them after 5 seconds by default, Gunicorn after 2. The ALB sends a request on a socket the target just closed. The 502s are scattered and rare, and in the access logs target_processing_time is -1.
2. Nothing listens on the target port. The app crashed or the target group points at the wrong port. Test from inside the VPC:
curl -sv http://10.0.1.12:8080/health3. TLS mismatch. An HTTPS target group in front of an app that speaks plain HTTP, or the reverse.
4. Deploys. Targets deregistered with requests still running past the deregistration delay (300 seconds by default).
5. Lambda targets. A response over 1 MB, a function timeout or an error.
If your targets are failing health checks, start with their health:
aws elbv2 describe-target-health --target-group-arn "$TG_ARN" \
--query "TargetHealthDescriptions[].[Target.Id,TargetHealth.State,TargetHealth.Reason]" --output tableAn unhealthy target group is covered in target group unhealthy.
How to fix
- Check
HTTPCode_ELB_502_CountagainstHTTPCode_Target_5XX_Countin CloudWatch to confirm the ALB is the source. - Set the target's keep-alive above the idle timeout:
server.keepAliveTimeout = 65000in Node.js,--keep-alive 65in Gunicorn,keepalive_timeout 65s;if nginx runs on the target. - Match the target group's protocol and port to what the app listens on.
- Raise the deregistration delay above your longest request, and let new targets pass health checks before old ones drain.
- For Lambda, keep responses under 1 MB and look at the function's errors and duration.
504 Gateway Time-out
The ALB returns 504 when it cannot connect to the target within 10 seconds, or when the target sends nothing before the idle timeout. Speed up the route, or raise idle_timeout.timeout_seconds for legitimately long requests. An HTTP monitor treats that 504 like any status code you did not expect, and rechecks it from your other regions before an outage opens.
Is it just you?
Test the public URL from several regions with Is my site down? to see whether every ALB node is affected.
Get alerted next time
- An HTTP monitor fails when the ALB answers 502 or 504 instead of the status code you expect.
- Each failure is rechecked from your other regions before an outage opens, so one reset connection does not page anyone.
- A TCP port monitor on a public target or a dependency's port tells you whether it still accepts connections.
FAQ
How do I know if the 502 comes from the ALB or from my app? ▼
Check the Server response header: awselb/2.0 means the load balancer built the 502. In CloudWatch, ALB-generated errors count in HTTPCode_ELB_502_Count, while a 502 your app returned counts in HTTPCode_Target_5XX_Count. In the access logs, elb_status_code is 502 and target_status_code is a dash when the target never answered.
Do unhealthy targets cause ALB 502 errors? ▼
Indirectly. If every target fails its health check, the ALB fails open and keeps routing to all of them, so you see the 502s or 504s those targets produce. A target group with no registered targets gets a 503 instead.
What is the default idle timeout of an Application Load Balancer? ▼
60 seconds, set by the idle_timeout.timeout_seconds attribute. Keep your targets' keep-alive timeout longer than this value so the target never closes a connection the ALB is about to reuse.