```html

Building a Serverless E-Commerce Site with Lambda, S3, and CloudFront: The adamcherrycomics.com Deployment

This post documents the complete deployment pipeline for adamcherrycomics.com, a serverless e-commerce site hosted on AWS infrastructure with a custom domain pointing through Route53. We'll cover the architecture decisions, Lambda function setup, S3 asset management, and CloudFront distribution configuration that made this deployment possible.

What Was Done

  • Built a complete static site with dynamic checkout capability using AWS Lambda
  • Configured S3 buckets for static content and Lambda deployment packages
  • Set up CloudFront distribution with ACM SSL certificate for custom domain
  • Created Lambda Function URL for serverless checkout processing
  • Implemented DNS validation and certificate management across multiple domain registrars
  • Deployed Stripe integration for payment processing

Site Architecture Overview

The site follows a hybrid static/serverless architecture:

  • Static pages (index.html, about-artist.html, shop.html, contact.html, shipping.html) served from S3 via CloudFront
  • Dynamic checkout handler deployed as AWS Lambda with a Function URL endpoint
  • Assets (product images, artist photos) stored in S3 and distributed through CloudFront CDN
  • DNS routed through Route53 with SSL termination at CloudFront

This approach provides scalability for static content while keeping operational complexity low through Lambda's serverless model.

File Structure and Deployment

All site files are organized in a local repository structure:

/Users/cb/Documents/repos/sites/adamcherrycomics.com/
├── index.html
├── about-artist.html
├── shop.html
├── contact.html
├── shipping.html
├── checkout-return.html
├── assets/
│   └── [product images, artist photos]
└── lambda/
    ├── checkout.py
    └── [stripe library and dependencies]

The HTML files were written with embedded Lambda Function URL for the checkout endpoint. This eliminates the need for a separate API gateway or load balancer—the Lambda Function URL handles HTTP requests directly.

Lambda Function Setup

The checkout handler lives in lambda/checkout.py and processes two endpoints:

  • POST /checkout – Creates Stripe checkout sessions
  • POST /subscribe – Handles subscription purchases

The Lambda function is deployed as a deployment package containing:

  • Python 3.x runtime with Flask or native HTTP handling
  • Stripe Python library installed via pip into the deployment directory
  • Environment variables for Stripe API keys (injected via Lambda configuration, not hardcoded)

Deployment steps:

# Install dependencies into lambda directory
pip install stripe -t ./lambda/

# Create deployment package
zip -r lambda-deployment.zip lambda/

# Create or update Lambda function
aws lambda create-function \
  --function-name adamcherrycomics-checkout \
  --runtime python3.11 \
  --handler checkout.lambda_handler \
  --zip-file fileb://lambda-deployment.zip \
  --role arn:aws:iam::ACCOUNT_ID:role/lambda-execution-role

# Create Function URL for direct HTTP invocation
aws lambda create-function-url-config \
  --function-name adamcherrycomics-checkout \
  --auth-type NONE \
  --cors '{"AllowOrigins":["https://adamcherrycomics.com","https://www.adamcherrycomics.dangerouscentaur.com"],"AllowMethods":["POST"]}'

The Function URL CORS configuration is critical—it restricts POST requests to the specific domains serving the site, preventing unauthorized checkout requests from other origins.

S3 and CloudFront Configuration

Static assets are stored in S3 and distributed through CloudFront with the following flow:

  1. HTML files uploaded to S3 bucket adamcherrycomics.com
  2. Product images and artist photos uploaded to assets/ prefix in the same bucket
  3. CloudFront distribution configured with S3 bucket as origin
  4. Cache invalidations applied after each deployment to bust outdated content
# Upload HTML files to S3
aws s3 cp index.html s3://adamcherrycomics.com/
aws s3 cp about-artist.html s3://adamcherrycomics.com/
aws s3 cp shop.html s3://adamcherrycomics.com/

# Upload assets
aws s3 sync assets/ s3://adamcherrycomics.com/assets/ --delete

# Invalidate CloudFront cache
aws cloudfront create-invalidation \
  --distribution-id E1A2B3C4D5E6F7 \
  --paths "/*"

Cache invalidations are applied to all paths (/*) to ensure users receive fresh content immediately after deployment. This is critical for e-commerce where pricing or product details might change.

DNS and SSL Certificate Management

The site needed to serve under two domains:

  • adamcherrycomics.com (primary domain)
  • www.adamcherrycomics.dangerouscentaur.com (subdomain of dangerouscentaur.com registrar account)

This required certificate management across multiple registrars:

  1. Requested ACM certificate covering both domains using DNS validation
  2. Added CNAME validation records to Route53 for adamcherrycomics.com
  3. Added CNAME validation records to Namecheap DNS (via API) for dangerouscentaur.com validation
  4. Updated CloudFront distribution to use the new certificate and add domain aliases
# Request certificate covering both domains
aws acm request-certificate \
  --domain-name adamcherrycomics.com \
  --subject-alternative-names www.adamcherrycomics.dangerouscentaur.com \
  --validation-method DNS

# Check certificate validation status
aws acm describe-certificate \
  --certificate-arn arn:aws:acm:us-east-1:ACCOUNT_ID:certificate/CERT_ID

# Update CloudFront distribution with new certificate
aws cloudfront update-distribution \
  --id E1A2B3C4D5E6F7 \
  --distribution-config file://distribution-config.json

The distribution-config.json file includes the new ACM certificate ARN and domain aliases. This must be fetched, modified locally, and reapplied with the correct ETag to avoid race conditions.

Key Decisions and Rationale

Why Lambda Function URL instead of API Gateway? Function URLs reduce complexity and cost. They're ideal for simple request/response patterns like checkout. API Gateway adds unnecessary overhead for a single-endpoint handler.

Why S3 + CloudFront instead of Lambda for static pages? S3 with CloudFront provides superior performance for static content, lower latency through edge locations, and zero compute cost. Lambda is better suited