My site, stephenthorn.com, cost $0.58 to host from March to September 2026. That covers S3, CloudFront and the small Lambda behind my contact form. The worst month was $0.19. It serves 20,000 to 25,000 requests a month, and CloudFront's share of the bill was $0.00.
This works for static sites: pages, images, blog posts. A login, a database or a form needs something extra (mine uses AWS Lambda, which only runs when someone submits).
What you need
An AWS account, with the AWS CLI installed and logged in on your computer
Claude Code
A domain name (a separate yearly cost, wherever you buy it)
The three pieces
S3 bucket: holds the site's files.
CloudFront: serves the site fast worldwide, with HTTPS for your domain.
DNS: points your domain at CloudFront. I use Cloudflare's free plan.
The setup prompt
Paste this into Claude Code in an empty folder:
Build my site as a static site with Astro. Then host it on AWS: an S3 bucket with versioning on, CloudFront in front of it with HTTPS for my domain, and a deploy.sh that builds, uploads without --delete, re-uploads HTML with no-cache, and invalidates CloudFront. Walk me through the DNS record I need to add at my domain provider.
Claude will ask for your domain and create the bucket, the certificate and the CloudFront distribution. Approve each AWS command as it runs.
My deploy script
Swap in your bucket, distribution ID and AWS profile:
#!/usr/bin/env bash
# Deploy the site to S3 + CloudFront. Usage: ./deploy.sh
set -euo pipefail
BUCKET="s3://your-domain.com"
DIST_ID="YOUR_CLOUDFRONT_ID"
PROFILE="default"
echo "Building..."
npm run build
echo "Uploading files (no --delete)..."
aws s3 sync dist/ "$BUCKET" --profile "$PROFILE"
echo "Re-uploading HTML with no-cache so pages are never stale..."
aws s3 cp dist/ "$BUCKET/" --recursive --exclude "*" --include "*.html" --content-type "text/html; charset=utf-8" --cache-control "no-cache, no-store, must-revalidate" --metadata-directive REPLACE --profile "$PROFILE"
echo "Clearing the CloudFront cache..."
aws cloudfront create-invalidation --distribution-id "$DIST_ID" --paths "/*" --profile "$PROFILE"
The mistake to avoid
On 28 July I deployed from a second computer with --delete on. That flag removes anything in the bucket that isn't in the new build, and that build was incomplete, so the live site disappeared. My bucket had no versioning, so S3 couldn't restore it.
Turn on versioning before your first upload.
Leave --delete out of your deploy script. Old files sitting in the bucket cost fractions of a cent.