Back to blog
AWSBest PracticesCloud SecurityReliabilityStorage

CloudFront Origin Bucket Does Not Exist: Fixing Orphaned S3 Origins

A CloudFront distribution pointing at a deleted S3 bucket risks outages and subdomain takeover. Learn how to detect, fix, and prevent orphaned origins on AWS.

TL;DR

This check flags CloudFront distributions whose S3 origin bucket has been deleted, which leaves you serving errors to users and exposes you to subdomain takeover if an attacker re-creates the bucket name. Recreate the bucket, point the origin somewhere valid, or remove the dead origin.

A CloudFront distribution is only as good as its origin. When the S3 bucket behind it gets deleted but the distribution keeps pointing at it, you end up with a CDN that confidently routes traffic to nowhere. This is the kind of misconfiguration that hides quietly until a customer reports a broken image, or worse, until someone exploits it.

This Lensix check, cloudfront_orphaneds3 in the cloudfront_checks module, scans your CloudFront distributions and verifies that every S3 origin still resolves to a bucket that actually exists in your account.


What this check detects

CloudFront distributions have one or more origins. For S3-backed origins, the distribution stores the bucket's domain name (something like my-assets-bucket.s3.amazonaws.com). Lensix walks through each distribution, extracts the S3 origins, and confirms the referenced bucket still exists.

If the bucket has been deleted, renamed, or moved to another account, the origin is considered orphaned and the check fails.

Note: CloudFront does not validate that an origin exists when you delete a bucket. The distribution config keeps the stale reference indefinitely. AWS treats the origin domain as just a string, so nothing stops you from pointing at a bucket that vanished months ago.


Why it matters

An orphaned origin is more than a cosmetic problem. There are two distinct risks, and the second one is serious.

1. Broken delivery

The obvious issue: requests routed to the missing bucket return errors. Depending on your origin configuration, users see 403 or 404 responses, or CloudFront error pages. If this origin serves a high-traffic path, you have an outage that monitoring may not catch immediately because the CDN itself is healthy.

2. Subdomain takeover (the dangerous one)

S3 bucket names are global. When you delete a bucket, the name becomes available for anyone to claim. If your CloudFront distribution still points at my-assets-bucket.s3.amazonaws.com and an attacker creates a bucket with that exact name, they now control what your distribution serves.

Danger: A claimed orphan bucket lets an attacker serve arbitrary content under your domain. That means defacement, phishing pages on your trusted hostname, malicious JavaScript injected into pages your users already trust, and cookie theft if the content runs in your origin's security context. This is a real, repeatedly-exploited class of attack.

The blast radius depends on what your CloudFront distribution fronts. If it serves your application's static assets or scripts, an attacker controlling that origin can run code in your users' browsers under your domain name. That is about as bad as it gets for a web app.


How to fix it

First, find which distribution and origin are affected. List your distributions and inspect their origins.

aws cloudfront list-distributions \
  --query "DistributionList.Items[].{Id:Id, Domain:DomainName, Origins:Origins.Items[].DomainName}" \
  --output json

Pick out any S3 origin domain, then check whether the bucket still exists:

# Replace with the bucket name from the origin domain
aws s3api head-bucket --bucket my-assets-bucket 2>&1

A 404 or NoSuchBucket response confirms the origin is orphaned. From here you have three valid paths.

Option A: Recreate the bucket (if the deletion was a mistake)

If the bucket was deleted accidentally and you still have the content backed up, recreate it with the same name in the correct region and restore the objects.

aws s3api create-bucket \
  --bucket my-assets-bucket \
  --region us-east-1

# Restore content from your backup
aws s3 sync ./backup/ s3://my-assets-bucket/

Warning: Recreating the bucket only works if no one else has already claimed the name. If create-bucket returns BucketAlreadyExists, treat it as a potential takeover, remove the origin from your distribution immediately, and audit what that origin was serving.

Option B: Point the origin at a valid bucket

If the content lives in a different bucket now, update the distribution to reference the correct origin. Export the current config, edit the origin domain, and apply it back.

# Get the current config and ETag
aws cloudfront get-distribution-config --id E1ABCDEF2GHIJK > dist-config.json

# Note the ETag value printed at the top of the file

Edit the DomainName for the affected origin in the DistributionConfig block, then update:

# Pass only the DistributionConfig object and the ETag as the IfMatch value
aws cloudfront update-distribution \
  --id E1ABCDEF2GHIJK \
  --distribution-config file://distribution-config-only.json \
  --if-match E2QWERTY3456

Danger: Updating a production distribution changes live traffic delivery. Confirm the new origin bucket exists, has the correct objects, and grants CloudFront read access (via Origin Access Control) before applying. A bad origin swap can take down delivery instantly.

Option C: Remove the dead origin

If the origin is no longer needed, remove it from the distribution config along with any cache behaviors that reference it. Edit the same exported config, delete the orphaned entry from Origins.Items, adjust Origins.Quantity, and apply the update the same way as Option B.

Doing it in Terraform

If your distributions are managed with Terraform, fix it at the source rather than in the console. Update the origin block to a bucket that exists and let your pipeline reconcile state.

resource "aws_cloudfront_distribution" "assets" {
  origin {
    domain_name              = aws_s3_bucket.assets.bucket_regional_domain_name
    origin_id                = "s3-assets"
    origin_access_control_id = aws_cloudfront_origin_access_control.assets.id
  }

  # ... default_cache_behavior, etc.
}

Tip: Reference the bucket through a resource attribute like aws_s3_bucket.assets.bucket_regional_domain_name instead of hardcoding a domain string. Terraform then refuses to create the distribution if the bucket resource is gone, turning a runtime risk into a plan-time error.


How to prevent it from happening again

Orphaned origins almost always come from one of two places: someone deleting a bucket by hand without checking what depended on it, or Terraform state drift where a bucket got removed but the distribution stayed. Close both gaps.

  1. Manage origins and buckets in the same IaC module. When the bucket and distribution live in the same Terraform configuration and reference each other by attribute, you cannot destroy one without the plan flagging the dependency.
  2. Gate destroys in CI/CD. Require a terraform plan review on every change. A plan that destroys an S3 bucket while a distribution still references it should block the merge.
  3. Run a policy check on apply. Use Open Policy Agent or a similar tool to reject any config where a CloudFront S3 origin does not map to a known bucket resource.

A simple OPA/Conftest rule against a Terraform plan can catch a distribution origin that no longer has a matching bucket:

package cloudfront

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_cloudfront_distribution"
  origin := resource.change.after.origin[_]
  endswith(origin.domain_name, ".s3.amazonaws.com")
  not bucket_exists(origin.domain_name)
  msg := sprintf("CloudFront origin %q has no matching managed S3 bucket", [origin.domain_name])
}

Tip: Lensix runs this check continuously, so even buckets deleted outside your IaC workflow get flagged. Wire the finding into Slack or your ticketing system so an orphaned origin becomes an alert within minutes instead of a customer complaint next week.


Best practices

  • Enable S3 versioning and MFA delete on origin buckets so accidental deletes are recoverable and harder to trigger.
  • Use Origin Access Control (OAC) instead of public buckets. This does not prevent orphaning, but it means a re-created malicious bucket cannot serve content unless it also grants access to your specific distribution.
  • Audit origins regularly. Distributions accumulate origins over years. Periodically reconcile every origin against live infrastructure and remove what is unused.
  • Treat bucket names as long-lived identifiers. Once a name is wired into a distribution, deleting it without removing the reference is a security event, not just cleanup. Document this in your runbooks.
  • Monitor CloudFront error rates. A spike in 4xx responses from a specific behavior often points straight at a broken origin before anyone notices the misconfiguration.

An orphaned CloudFront origin sits at the intersection of a reliability bug and a takeover vector. Fixing the immediate finding takes minutes. Making sure it never recurs takes a little discipline in how you couple buckets and distributions, and that discipline pays for itself the first time it blocks a bad destroy.