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
E2Q4UU71SRNTMBhas 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