```html

Consolidating Multi-Site Infrastructure: Migrating adamcherrycomics from Per-Bucket to Shared Origin Pattern

What Was Done

We completed a infrastructure consolidation for adamcherrycomics.dangerouscentaur.com, removing a stale per-site S3 bucket and confirming the site now operates entirely on a shared origin pattern. The site had been successfully migrated to the shared dc-sites bucket in a previous session, but the legacy bucket remained in AWS with zero objects. We verified it was safe to delete, removed it, and validated that all traffic routing continues to work correctly through CloudFront.

Why This Matters

Legacy multi-site deployments often accumulate orphaned AWS resources as infrastructure patterns evolve. The per-bucket model (one S3 bucket per site) scales poorly: more buckets mean more IAM policies, more billing line items, more CloudFront origins to manage, and more DNS records to maintain. A shared-origin pattern with intelligent request routing eliminates this overhead. However, the migration process can leave behind empty buckets that are forgotten. Confirming deletion safety and cleaning these up reduces operational debt and clarifies the actual infrastructure topology for future engineers.

Technical Details: Verification Before Deletion

Before removing the bucket s3://adamcherrycomics.dangerouscentaur.com/, we confirmed three conditions:

  • Bucket is genuinely empty: AWS S3 API returned Total Objects: 0. No versioned or non-versioned objects remained.
  • Not on the serving path: DNS for adamcherrycomics.dangerouscentaur.com points to dclu4nl5nln98.cloudfront.net (CloudFront distribution E2Q4UU71SRNTMB). The distribution has a single origin: dc-sites.s3.us-east-1.amazonaws.com. A CloudFront Function named dc-sites-router inspects the Host header and rewrites the S3 key path to dc-sites/adamcherrycomics.dangerouscentaur.com/..., routing all requests to the shared bucket. The per-site bucket was never consulted.
  • No DNS or CNAME dependency: No other services reference the old bucket endpoint. The domain CNAME is to CloudFront, not to an S3 website endpoint like adamcherrycomics.dangerouscentaur.com.s3-website-us-east-1.amazonaws.com.

Infrastructure: Current State

After deletion, the active infrastructure for adamcherrycomics.dangerouscentaur.com is:

DNS (Namecheap):
  adamcherrycomics.dangerouscentaur.com CNAME → dclu4nl5nln98.cloudfront.net

CloudFront Distribution E2Q4UU71SRNTMB:
  Origin: dc-sites.s3.us-east-1.amazonaws.com
  Function (Viewer Request): dc-sites-router
    - Extracts Host header
    - Rewrites S3 URI: /path → /adamcherrycomics.dangerouscentaur.com/path
  
S3 Shared Bucket: dc-sites
  Prefix: /adamcherrycomics.dangerouscentaur.com/
    ├── index.html
    ├── shop.html
    ├── about-artist.html
    └── styles/, images/, assets/

Deleted:
  s3://adamcherrycomics.dangerouscentaur.com/ (empty, 0 objects)

Key Architectural Decision: Why Shared Origin?

The dc-sites shared-bucket pattern with a CloudFront Function router offers several advantages over per-site buckets:

  • Single CloudFront origin: All *.dangerouscentaur.com subdomains share one distribution, one certificate, one WAF policy, one set of invalidation targets, and one cache behavior set. Adding a new site no longer requires a new CF distribution.
  • Simplified IAM: One bucket policy and one IAM role for all sites, rather than N policies for N buckets.
  • Reduced blast radius: A misconfigured bucket policy on one site no longer affects others; the policy is shared, but the S3 key prefixes provide hard boundaries.
  • Efficient invalidation: Instead of invalidating across multiple distributions, a single aws cloudfront create-invalidation --distribution-id E2Q4UU71SRNTMB --paths "/*" refreshes all sites at once.
  • Cleaner billing: One S3 bucket instead of N, one CloudFront distribution instead of N.

The dc-sites-router CloudFront Function (Viewer Request stage) intercepts every request, extracts the hostname from the Host header, and rewrites the request URI transparently. S3 receives the rewritten path and serves the correct content. From the client's perspective, it's one distribution; from AWS's perspective, it's one billing entity.

Deletion Process

Once verified safe, deletion was straightforward:

# Confirm bucket is empty
aws s3api head-bucket --bucket adamcherrycomics.dangerouscentaur.com

# List any objects (should return empty)
aws s3api list-objects-v2 --bucket adamcherrycomics.dangerouscentaur.com

# Delete the bucket
aws s3api delete-bucket --bucket adamcherrycomics.dangerouscentaur.com --region us-east-1

The deletion succeeded immediately because the bucket had no objects and no versioning enabled.

Verification After Deletion

We confirmed the site continued to serve 200 responses:

curl -I https://adamcherrycomics.dangerouscentaur.com/
# HTTP/2 200
# Content-Type: text/html; charset=utf-8
# (served from dc-sites bucket via CF distribution E2Q4UU71SRNTMB)

All pages (Shop, About Artist, index) returned correct content. Stripe Hosted Checkout links worked. No user impact.

What's Next

  • Document the pattern: Ensure the dc-sites-router architecture is documented in the team's infrastructure as code or runbook so future engineers understand why there's one CloudFront distribution and one bucket for multiple sites.
  • Apply to other sites: If other dangerouscentaur.com sites still use per-bucket patterns, apply the same consolidation.
  • Monitor for orphans: Periodically audit S3 buckets for empty or unused resources, especially after migrations, to prevent similar technical debt accumulation.
  • End-to-end testing: Confirm with adamcherrycomics stakeholders that the full Buy Now → Stripe Hosted Checkout → return flow works end-to-end; this is the next blocking item on the roadmap.

Takeaway

Consolidating multi-