Fix 502, 503 and 504 from an Application Load Balancer

Decide whether the load balancer or the target produced the error from the access log and metrics, then follow the ranked causes for 502 (connection reset, keep-alive shorter than idle timeout, bad response, TLS to target, Lambda), 503 (no healthy or registered targets) and 504 (connect or idle timeout, NACL return path).

markdownguidev43TNR-J9EEstage: operatetrust: officialservice: elbinterface: cliALB 502 Bad GatewayALB 503 Service UnavailableALB 504 Gateway Timeoutelb_status_codetarget_status_codeHTTPCode_ELB_5XX_CountHTTPCode_Target_5XX_CountTargetConnectionErrorCountkeep-alive timeoutidle timeout 60 secondsderegistration delayunhealthy targetsLambdaUnhandled

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

First find out who produced the status. The load balancer forwards a target's own 5xx unchanged. Those are application bugs. The 502, 503 and 504 that the load balancer generates mean it could not get a usable answer from a target. Only those are covered here.

Step 0: load balancer or target?

EvidenceLoad balancer generatedTarget generated
Access log elb_status_code / target_status_code502 / -502 / 502
MetricHTTPCode_ELB_5XX_Count (HTTPCode_ELB_502_Count…)HTTPCode_Target_5XX_Count

A target-generated error belongs to the application: read its logs. Turn on access logs if they are off (load balancer attribute access_logs.s3.enabled, default false, per the ELBv2 API model; also set access_logs.s3.bucket).

503: nothing to send to

The target group has no registered targets, or all of them are unused. That means not registered, not in a target group used by a listener rule, in an AZ the load balancer doesn't have enabled, or stopped.

aws elbv2 describe-target-health --target-group-arn <tg> \
  --query 'TargetHealthDescriptions[].[Target.Id,TargetHealth.State,TargetHealth.Reason,TargetHealth.Description]'

The Reason code says which case it is (reference/elb/errors.md). If every target is unhealthy, the load balancer fails open and sends to all of them anyway. So a total health-check failure produces 502/504 from broken targets, not 503.

502: the target answered badly, or hung up

Ranked by how often each is the cause:

  1. The target's keep-alive timeout is shorter than the load balancer's idle timeout. The target closes an idle connection the load balancer is about to reuse. The load balancer's idle timeout defaults to 60 s. Set the application server's keep-alive timeout longer than that, or lower the load balancer's: aws elbv2 modify-load-balancer-attributes --load-balancer-arn <lb> --attributes Key=idle_timeout.timeout_seconds,Value=<seconds> (range 1–4000).
  2. Connection refused or reset (TCP RST), or "host unreachable" while connecting. Nothing is listening on the target port, or the security group or NACL blocks the load balancer's subnets. In the access log, the processing times are -1. The metric is TargetConnectionErrorCount.
  3. Malformed response: invalid headers, or response headers over 32 KB in total.
  4. TLS to the target fails (HTTPS target group). The target's certificate, key format or ciphers don't match the backend policy the load balancer chose from its front-end policy (ELBSecurityPolicy-TLS13-1-0-2021-06 for TLS 1.3 listeners, ELBSecurityPolicy-2016-08 for most others).
  5. A target was deregistered mid-request and the deregistration delay ran out. Raise the delay for long requests. CloudTrail shows DeregisterTargets.
  6. Lambda targets: a response body over 1 MB (error_reason LambdaResponseTooLarge), the function timed out (LambdaUnhandled), the function threw, or it was throttled. See guides/lambda/debug-failing-function.md.

504: the target didn't answer in time

  1. Couldn't connect within 10 seconds. The usual cause is a security group on the target that doesn't allow the load balancer's security group on the traffic port.
  2. Connected, but no response before the idle timeout. A slow request: fix the application, or raise idle_timeout.timeout_seconds.
  3. The NACL on the target subnet blocks the return path: ephemeral ports 1024–65535 back to the load balancer's subnets.
  4. The target sent a Content-Length larger than the body it sent.
  5. A Lambda target didn't respond before the connection timeout.
  6. The TLS handshake to the target took more than 10 seconds.

Checks in order

  1. describe-target-health: are targets healthy, and if not, which reason code?
  2. From a host in the load balancer's subnets, curl -v http://<target-private-ip>:<port><health-path>. Does the target answer, and how quickly?
  3. Security groups: the target's group allows the load balancer's group on the traffic and health-check ports.
  4. NACLs: health-check and traffic ports inbound on the target subnets, and ephemeral ports both ways.
  5. Timeouts: the application's keep-alive is longer than the load balancer's idle timeout, and request time is shorter than it.
  6. Reachability Analyzer from the load balancer's network interface to the target (guides/vpc/diagnose-connection-timeout.md).

Other load balancer statuses worth recognising

  • 460: the client closed the connection before the load balancer's idle timeout. The client's timeout is shorter.
  • 463: the X-Forwarded-For header carries more than 30 IPs.
  • 464: the request's protocol doesn't match the target group's protocol version (HTTP/1.1 vs HTTP/2 or gRPC).
  • 000 with HTTP/2: the load balancer closes a connection after 10,000 requests (GOAWAY).

The rest are in reference/elb/errors.md.

Read this document as markdown — the same bytes an agent receives.