QUANHEX.
AWS

Deploying an Astro Site to AWS S3 With Cloudflare

Step-by-step architecture for hosting an Astro static site on AWS S3, served through Cloudflare's CDN — near-zero cost, global performance, and automatic deploys.

· 6 min read

Static site hosting is a solved problem, but the implementation details matter. The right stack gives you global CDN performance, automatic HTTPS, and continuous deployment for a monthly cost that rounds to zero. Here’s the architecture we use.

Why This Stack

Astro compiles to static HTML, CSS, and minimal JavaScript. The output is CDN-friendly by nature — no server-side rendering required for a marketing site or blog.

S3 stores the static files. At marketing site scales, you’ll stay within AWS’s free tier indefinitely. S3 is not a CDN — it’s the origin.

Cloudflare is the CDN layer. It caches your S3 files at the edge, handles HTTPS termination, provides DDoS protection, and lets you manage your DNS. The free tier covers everything you need.

S3 Configuration

Create your bucket with the same name as your domain. Enable static website hosting, set index.html as the index document and 404.html as the error document.

Public access requires intentionally removing S3’s default public access blocks and applying a bucket policy that allows s3:GetObject for all principals. This is safe because Cloudflare is your public-facing layer — S3 is just the origin.

Cloudflare DNS Setup

Add a CNAME record pointing your domain to the S3 website endpoint:

  • @yourdomain.com.s3-website-us-east-1.amazonaws.com
  • Proxy status: ON (orange cloud)

With proxying on, Cloudflare terminates HTTPS and caches responses at the edge. Your SSL mode should be Flexible since S3 website endpoints don’t natively support HTTPS.

Cache Headers

The deploy script sets aggressive cache headers on built assets:

aws s3 sync dist/ s3://yourdomain.com \
  --cache-control "public, max-age=31536000, immutable"

Astro adds content hashes to all asset filenames (CSS, JS, images). A file named style.a1b2c3.css will never change its content, so a one-year cache is safe. HTML files should have shorter cache headers since they reference the hashed assets.

GitHub Actions Deploy

The full deploy pipeline on every push to main:

  1. npm ci — install dependencies
  2. npm run build — Astro builds to dist/
  3. aws s3 sync dist/ s3://bucket --delete — sync and remove stale files
  4. Cloudflare API call to purge the edge cache

The --delete flag ensures removed pages disappear from S3. The cache purge ensures Cloudflare serves the new version immediately.

Lighthouse Scores

A well-configured Astro site on this stack scores consistently well on Lighthouse. Astro ships zero JavaScript by default — the only JS is what you explicitly add. Combined with proper cache headers, font preloading, and semantic HTML, 100s across Performance, Accessibility, Best Practices, and SEO are achievable with discipline.

The stack costs you the Route 53 equivalent (nothing if you manage DNS in Cloudflare), S3 storage (cents per month), and S3 request costs (fractions of cents). For a consulting or marketing site, this rounds to zero.