Amazon CloudFront errors
Look up a CloudFront error by status and symptom — 400 and 412, 403 (alternate domain name, WAF, S3 origin access, geo restriction, signed URLs, stacked distributions), 502 (origin TLS name or chain, ciphers, ports, NonS3OriginDnsError, function validation), 504 (firewall, private origin, slow origin), 500 and 503 (origin capacity, S3 prefix rates, edge functions) — with the check and the fix for each.
markdownreferencev4PVW8-NAG3stage: operatetrust: officialservice: cloudfrontinterface: anyCloudFront 400CloudFront 412CloudFront 500CloudFront 503The authorization header is malformed the region is wrongCloudFront 403CloudFront 502CloudFront 504X-Cache Error from cloudfrontNonS3OriginDnsErrorThe request could not be satisfiedalternate domain nameCNAME 403OAC 403WAF 403geo restrictionsigned URL 403stacked distributionsorigin certificate mismatchorigin response timeoutCloudFront IP rangesorigin-facing prefix list
First decide whether CloudFront itself refused the request or relayed the origin's answer. Call the origin directly with the same path and Host header. If the error reproduces there, the origin produced it. A 502 that CloudFront generates carries X-Cache: Error from cloudfront.
403 Forbidden
| Cause | Fix | Check |
|---|---|---|
| DNS points at CloudFront, but the domain isn't an alternate domain name on the distribution | Add it (the certificate must cover it) | aws cloudfront get-distribution-config --id <d> --query DistributionConfig.Aliases |
| AWS WAF blocked it, at CloudFront or at the origin | CloudFront can't tell a WAF 403 from an origin 403. Check the web ACL's sampled or blocked requests | aws cloudfront get-distribution-config --id <d> --query DistributionConfig.WebACLId, then the ACL's sampled requests (aws wafv2 get-sampled-requests) |
| S3 origin: CloudFront has no access | Private bucket without OAC/OAI on the origin, or a bucket policy missing the cloudfront.amazonaws.com statement with this distribution's AWS:SourceArn (guides/cloudfront/serve-s3-privately.md). SSE-KMS objects also need the key-policy statement | aws cloudfront get-distribution-config --id <d> --query DistributionConfig.Origins → OriginAccessControlId; aws s3api get-bucket-policy --bucket <b> |
| S3 origin: wrong path or missing object | Without s3:ListBucket, S3 returns 403 instead of 404 for a missing key. A default root object or index document applies at the root only (UNVERIFIED in the collected sources) | aws s3api head-object --bucket <b> --key <key> with the origin path prepended |
| S3 origin: Host header forwarded | Don't forward the viewer's Host to an S3 REST endpoint (use an origin request policy that excludes it) | aws cloudfront get-origin-request-policy --id <id> → headers forwarded |
| Custom error pages configured | The error page itself may be missing or forbidden | aws cloudfront get-distribution-config --id <d> --query DistributionConfig.CustomErrorResponses |
| Custom origin firewall | The origin rejects CloudFront. Reproduce directly, then read the origin's logs | curl -sI https://<origin>/<path> from outside AWS, then the origin's logs |
| Geographic restriction | The viewer's country is blocked by design | aws cloudfront get-distribution-config --id <d> --query DistributionConfig.Restrictions |
| Restrict viewer access on the behaviour | The request lacks a valid signed URL or signed cookie | aws cloudfront get-distribution-config --id <d> --query DistributionConfig.DefaultCacheBehavior.TrustedKeyGroups (and each CacheBehaviors entry) |
| Stacked distributions (a distribution whose origin is another distribution) | Not supported: returns 403 | aws cloudfront get-distribution-config --id <d> --query DistributionConfig.Origins: a *.cloudfront.net domain |
502 Bad Gateway: couldn't use the origin
| Cause | Fix | Check |
|---|---|---|
Origin certificate doesn't match the origin domain name, or the Host header sent | Reissue the certificate with the right names, or change the origin domain | openssl s_client -connect <origin>:443 -servername <name>: subject and SANs |
| Origin certificate expired, invalid or self-signed, or the chain is incomplete or in the wrong order | Install the full chain from a CA on Mozilla's trusted list, intermediates included | openssl s_client -connect <origin>:443 -servername <name> -showcerts: Verify return code |
| No common cipher or protocol with CloudFront | Compare the origin's TLS configuration with CloudFront's supported origin ciphers | openssl s_client -connect <origin>:443 -tls1_2 (and -tls1_3) |
| Origin not listening on the configured ports (defaults 80/443), or a firewall blocks CloudFront's IP ranges | Allow the CloudFront origin-facing prefix list com.amazonaws.global.cloudfront.origin-facing (also com.amazonaws.global.ipv6.cloudfront.origin-facing for IPv6) in the origin's security group | aws ec2 describe-security-group-rules --filters Name=group-id,Values=<sg>: is the prefix list there? |
NonS3OriginDnsError | CloudFront couldn't resolve the origin name in public DNS. Fix the NS/SOA records | dig <apex> NS +short, then query each name server, from the internet |
| Lambda@Edge returned an invalid response | Fix the function's response shape | The function's logs in the Region nearest the viewer (/aws/lambda/us-east-1.<fn>) |
| A CloudFront Function changed a read-only header | Only shows after deployment, not in the test console | aws cloudfront test-function --name <fn> --if-match <etag> --event-object fileb://event.json |
| ALB or API Gateway origin returned 502 itself | guides/elb/fix-alb-5xx.md | curl -sI the origin directly: a 502 without CloudFront is the origin's |
504 Gateway Timeout: the origin didn't answer in time
Check access before performance:
- A firewall or security group blocks CloudFront. Allow the origin-facing prefix list, and keep IP allow-lists current. AWS publishes IP range changes on an SNS topic.
- The origin isn't reachable from the internet. A private server, or an internal load balancer used as an ordinary custom origin, can't be reached. Use a VPC origin for private ALBs, NLBs and EC2 instances (
patterns/cloudfront-to-alb.md). The 504 page's statement that private servers can't be origins predates VPC origins. - The origin is slow. Measure with
curl -w(time to first byte), add capacity, tune queries, and enable keep-alive. Then, if needed, raise the distribution's origin response timeout. - The origin's own timeouts. ALB origins: a 10 s connect timeout, the idle timeout, the NACL ephemeral return path,
Content-Lengthlarger than the body. API Gateway: the integration timeout.
Test: nc -zv <origin-domain> 443 from the internet.
VPC origin–specific
| Symptom | Fix | Check |
|---|---|---|
| VPC origin can't be created | The VPC needs an internet gateway attached (it isn't used for routing) and a free IP in the private subnet. Gateway Load Balancers and NLBs with TLS listeners aren't supported, and an NLB needs a security group | aws ec2 describe-internet-gateways --filters Name=attachment.vpc-id,Values=<vpc> |
| Timeouts to a VPC origin | The origin's security group must allow the origin-facing prefix list or the CloudFront-VPCOrigins-Service-SG group. The outbound NACL must allow ephemeral ports 1024–65535 for return traffic (inbound NACLs aren't evaluated) | aws ec2 describe-security-group-rules --filters Name=group-id,Values=<sg> |
| gRPC or Lambda@Edge origin triggers fail | Not supported with VPC origins | DefaultCacheBehavior.GrpcConfig and LambdaFunctionAssociations on the VPC-origin behaviour |
400, 412, 500, 503
| Status | Cause → fix | Check |
|---|---|---|
400 with The authorization header is malformed; the region '<r1>' is wrong; expecting '<r2>' | The S3 bucket was deleted and recreated with the same name in another Region. Edit the origin and save it unchanged ("Yes, Edit") to redeploy | aws s3api get-bucket-location --bucket <b> vs the Region in the message |
| 400 from an ALB origin | Malformed request; header limits (16 KB line, 16 KB header, 64 KB total); client closed early | The ALB access log's elb_status_code and request for that request |
| 412 | A conditional non-GET/HEAD request whose If-Unmodified-Since/If-None-Match failed; or a condition in an UpdateDistribution call evaluated false (a stale IfMatch ETag, UNVERIFIED example) | Replay the request with and without its conditional headers |
| 500 | Usually the origin's own 500 (S3, API Gateway, ELB). Otherwise an edge (PoP) error: contact Support | X-Cache and x-amz-cf-pop response headers; the origin's logs at that time |
| 503 | Origin capacity. For S3: 3,500 write and 5,500 read requests per second per prefix → spread keys across prefixes. Or unhealthy or overloaded ELB targets, an origin out of CPU or memory, an API Gateway backend. Rarely, edge capacity (common in load tests: follow CloudFront's load-testing guidance). Also a Lambda@Edge or CloudFront Function execution error, Lambda@Edge throttling or timeout, or an origin mTLS handshake failing repeatedly (temporary) | S3 5xxErrors request metrics, or the origin's own capacity metrics |
Certificate errors at the viewer
- Certificate not selectable on the distribution: it isn't in ACM us-east-1.
- Alternate domain name rejected: the certificate's SAN doesn't cover it.
- An imported certificate expiring: re-import at least 24 hours before
NotAfter; propagation can take up to 24 hours.
When none of these fits
Capture the response's x-amz-cf-id and x-cache headers and the time. Then test the origin directly, bypassing CloudFront. If the origin fails too, it's an origin problem (reference/elb/errors.md, reference/s3/bucket-and-object-errors.md); if not, compare the cache and origin request policies with what the origin needs. Standard logs for the distribution show the result type per request. AWS Support can trace a request by its x-amz-cf-id (UNVERIFIED).