A CloudFront 502 or 504 is almost never CloudFront’s fault. A 502 Bad Gateway means CloudFront reached out to your origin and the conversation failed — most often a TLS certificate on the origin that does not match the origin domain name, an expired certificate, or a connection reset. A 504 Gateway Timeout means the origin did not answer in time: either the TCP connection never established within the connection timeout, or the response did not start within the origin response timeout (30 seconds by default). Both are diagnosed the same way: take CloudFront out of the picture and talk to the origin directly.
What’s happening
When a request misses the cache, CloudFront opens a connection from an edge location to your origin using the origin domain name configured on the distribution — not the viewer’s hostname. That distinction drives the most common 502: CloudFront connects to origin.example.com over HTTPS, the origin presents a certificate for *.example.com or for the wrong site entirely, hostname verification fails, and CloudFront returns 502 to the viewer. Self-signed certificates, expired certificates, incomplete chains (missing intermediates), and origins that only speak TLS versions or ciphers CloudFront does not offer all land in the same bucket.
The 504 family is time-based. CloudFront attempts the TCP connection with a configurable connection timeout (default 10 seconds) and attempt count (default 3), then waits up to the origin response timeout for first bytes. Origins that are overloaded, doing slow database work, or behind a security group that silently drops CloudFront’s traffic produce 504s. A subtler one: the origin’s HTTP keep-alive idle timeout being shorter than CloudFront’s origin keep-alive timeout, so the origin closes reused connections mid-flight and requests fail intermittently — usually surfacing as sporadic 502s under steady traffic.
Every one of these is visible from the origin side, which is why the checklist below starts there.
What to check first
- Is the origin itself healthy? If the origin is an ALB, check its own 5xx and target health before blaming the CDN — an ALB returning 502/504 gets passed straight through.
- Does the origin certificate match the origin domain name configured on the distribution — not the viewer domain?
- Is the failure constant or intermittent? Constant points at TLS or security groups; intermittent points at timeouts, keep-alive mismatch, or an overloaded origin.
- Did anything change? Certificate renewals, security group edits, and origin-side deploys are the usual suspects on day one of a 502 storm.
How to verify
Reproduce CloudFront’s view of the origin from your own machine:
# Talk to the origin exactly as CloudFront does: origin domain name, HTTPS
curl -svo /dev/null https://origin.example.com/health --max-time 35
# Inspect the certificate the origin presents, including the chain
echo | openssl s_client -connect origin.example.com:443 \
-servername origin.example.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -enddate
# Time to first byte on the slow path CloudFront is timing out on
curl -so /dev/null -w 'connect=%{time_connect}s ttfb=%{time_starttransfer}s\n' \
https://origin.example.com/slow-endpoint
A certificate name mismatch, an expired notAfter, or a ttfb above 30 seconds reproduces the CloudFront error locally, and you can stop guessing. Then confirm what the edge is recording: CloudFront standard logs carry an x-edge-result-type of Error and an x-edge-detailed-result-type such as OriginConnectError, OriginTimeout, or OriginSSLError — that field alone splits 502 causes from 504 causes. The distribution’s 5xxErrorRate metric lives in CloudWatch under us-east-1, visible at CloudFront > Distributions > (id) > Monitoring.
Timeout and keep-alive settings sit at CloudFront > Distributions > (id) > Origins > Edit > Additional settings — response timeout, keep-alive timeout, connection attempts, and connection timeout are all per-origin.
The fix
- Fix the origin certificate. The certificate must be valid, unexpired, chained to a trusted CA with intermediates included, and cover the origin domain name exactly. On an ALB origin, that means the ALB’s certificate covers
origin.example.com; on a custom server, check the virtual host actually serving that name. This is the top cause of hard 502s. - Open the path. The origin’s security group or firewall must accept traffic from CloudFront. For an ALB origin, the practical pattern is allowing the CloudFront origin-facing managed prefix list on 443. A half-open rule produces timeouts, not refusals — which reads as 504.
- Raise the origin response timeout for genuinely slow endpoints. Report generation or large uploads that legitimately need more than 30 seconds justify raising the per-origin response timeout. It is a per-origin setting; raise it for the slow API origin, not for the static one.
- Align keep-alive timeouts. Set the origin’s idle timeout longer than CloudFront’s origin keep-alive timeout so the origin never hangs up first. On an ALB, that is the load balancer idle timeout attribute; on nginx,
keepalive_timeout. - Fix origin overload rather than masking it. If TTFB degrades under load, longer timeouts just queue pain. Scale the origin, tune the app, and make sure load balancer health checks evict bad targets fast enough that the ALB is not proxying to the dying instance.
- Match protocol expectations. Origin protocol policy HTTPS-only against an origin that only listens on 80 (or vice versa with redirects that CloudFront will not follow on the origin side) fails cleanly. Check the origin protocol policy against what the origin actually serves.
When this is the wrong move
If the error page says 500 or 503 rather than 502/504, the origin did answer — debug the application, not the connection. If errors correlate with an AWS-side event, check the AWS Health Dashboard before rearchitecting anything. And do not “fix” 504s by fronting the slow endpoint with a longer timeout indefinitely: past a point, the honest fix is asynchronous processing, because a viewer waiting 60 seconds for a synchronous response is a product defect, not an infrastructure setting.
Common mistakes
- Testing the viewer URL (which hits the cache and works) instead of the origin domain name, and concluding the origin is fine.
- Renewing the origin certificate but forgetting the intermediate chain, which passes browser spot-checks against the viewer domain while CloudFront keeps failing against the origin.
- Raising the response timeout on every origin as a reflex, hiding a degrading backend for months.
- Forgetting that an ALB origin has its own timeout and 5xx behavior — two proxies means two places to align.
Prevention
- Alarm on the distribution’s
5xxErrorRateand on origin latency percentiles, so timeout drift is visible before it crosses 30 seconds. - Monitor origin certificate expiry with ACM’s
DaysToExpirywhere applicable, and an external check where not. - Keep origin TLS and viewer-side certificates in the same renewal calendar — they fail on different schedules otherwise.
- Enable CloudFront standard logs to S3 before the incident;
x-edge-detailed-result-typeis worth little if logging starts after the fire. - Re-test the origin path after every security group or WAF change.
The per-origin timeout reference and result-type field definitions are in the CloudFront documentation. If nobody on your team truly owns the AWS environment end to end, that is the root of most of these problems — Stack Harbor runs AWS accounts daily under AWS Operations, and you can book an AWS review.