```html

Migrating a Stripe-Integrated E-Commerce Site to Shared CloudFront + Lambda Infrastructure

What Was Done

adamcherrycomics.dangerouscentaur.com is a Stripe-integrated e-commerce site selling original art and prints. Over the past development cycle, we consolidated it from a per-site bucket architecture onto a shared origin bucket with CloudFront routing logic, fixed critical checkout and DNS issues, and decommissioned the legacy empty bucket.

The Problem: Per-Site Buckets vs. Shared Infrastructure

The original architecture used individual S3 buckets per site: s3://adamcherrycomics.dangerouscentaur.com/ with direct CloudFront distributions. This pattern scales poorly—every new site requires:

  • A new S3 bucket (global namespace contention)
  • A new CloudFront distribution (cost and management overhead)
  • Separate DNS CNAME records and SSL certificates
  • Per-site Lambda@Edge or CloudFront Functions for routing logic

The solution: consolidate all *.dangerouscentaur.com sites onto a single shared bucket (s3://dc-sites) and use CloudFront Functions to route requests by hostname.

Architecture: Shared Origin + CloudFront Router

Origin: s3://dc-sites.s3.us-east-1.amazonaws.com

CloudFront Distribution: E2Q4UU71SRNTMB (shared across all dangerouscentaur.com sites)

Router Function: dc-sites-router (CloudFront Function, viewer-request trigger)

Request flow:

User → adamcherrycomics.dangerouscentaur.com
  ↓ (CNAME)
dclu4nl5nln98.cloudfront.net (Distribution E2Q4UU71SRNTMB)
  ↓ (dc-sites-router Function rewrites URI)
/adamcherrycomics.dangerouscentaur.com/index.html → dc-sites bucket
  ↓ (S3 origin serves object)
200 OK + HTML response

This means the DNS record at Namecheap is simply:

adamcherrycomics.dangerouscentaur.com  CNAME  dclu4nl5nln98.cloudfront.net

No direct S3 endpoint in DNS; no per-site bucket needed.

Critical Fixes: DNS Shadowing and Stripe Checkout

DNS Shadowing (RFC 1034 §4.3.3)

The www subdomain was unreachable because the wildcard CNAME at Namecheap was incomplete. RFC 1034 forbids CNAME records at the apex of a zone, but subdomains need explicit records or wildcard coverage. The old config had a wildcard but no explicit adamcherrycomics record, so queries for www.adamcherrycomics.dangerouscentaur.com matched the wildcard but the cloudfront endpoint rejected the mismatch between Host header and its valid names.

Fix: Added explicit adamcherrycomics A record (via Namecheap) pointing to CloudFront. Kept the wildcard for future sites.

Stripe Checkout Outage

The Lambda function handling /checkout POST requests was missing the typing_extensions module (required by stripe-python for type hints in v15.0+). Additionally, the code used deprecated ui_mode="embedded", which Stripe removed in stripe-python 15.1.0.

Fix: Rebuilt the Lambda deployment zip to include all dependencies, switched to ui_mode="hosted_page", and changed the frontend to redirect users to session.url instead of embedding a Stripe UI.

# Before (broken)
session = stripe.checkout.Session.create(
    ui_mode="embedded",
    ...
)
# Returns session.client_secret; frontend embeds via JS

# After (working)
session = stripe.checkout.Session.create(
    ui_mode="hosted_page",
    ...
)
# Frontend redirects to session.url directly

This is the simplest, most reliable pattern for Stripe hosted checkout and avoids maintaining Stripe.js embedding logic.

Decommissioning the Legacy Bucket

The bucket s3://adamcherrycomics.dangerouscentaur.com/ was orphaned—it contained zero objects and was never referenced by CloudFront (which uses the shared dc-sites bucket instead).

Verification:

  • CloudFront distribution E2Q4UU71SRNTMB has exactly one origin: dc-sites.s3.us-east-1.amazonaws.com
  • No DNS records point to an S3 website endpoint; all point to CloudFront
  • Bucket contained no objects; safe to delete without data loss

Action: Deleted s3://adamcherrycomics.dangerouscentaur.com/. Reverting is trivial (re-create empty bucket with same name), but no reason to keep it.

Content Updates

About Artist page: Updated artist portrait to IMG_2567.jpeg and changed the link from Instagram to canvasrebel.com (artist's main portfolio). The change is live across all four HTML pages (index, about, gallery, checkout-confirmation).

Dropdown hover bug: Fixed CSS gap calculation across all pages. The previous top: calc(100% + Npx) rule had inconsistent pixel offsets. Normalized to a single variable in the shared CSS partial.

Key Architectural Decisions

Why Shared Bucket + CloudFront Functions?

  • Cost: One distribution, one bucket, one set of Lambda logs instead of N of each
  • Scaling: Add new sites by creating a folder in dc-sites and adding a CNAME at the registrar
  • Simplicity: CloudFront Functions run at edge with no cold start (unlike Lambda@Edge), and routing logic is centralized and version-controlled
  • Consistency: All sites benefit from the same caching rules, HTTPS config, and security headers