Skip to content
jccbbb
Go back

Astro + Cloudflare: My Preferred Stack for Modern Content Sites

If you ask me to build a content site in 2026 — a blog, a marketing site, a small storefront, a docs portal, a portfolio — my answer is the same: Astro on Cloudflare Pages. I’ve shipped enough variations to know it’s not a fad.

Here’s what makes that pairing stick.

Table of contents

Open Table of contents

The case for Astro

Astro’s pitch is “ship less JavaScript.” It’s been the same pitch for four years, and the execution keeps getting better.

Three things that matter when I evaluate a content stack:

  1. HTML-first rendering. The default output is HTML with zero hydration. Components opt in to interactivity via client:load, client:idle, client:visible. Most marketing sites need none of it.
  2. Framework-agnostic islands. I can drop a React component, a Svelte component, and a Vue component into the same page if I have to. I rarely have to, but the option keeps me honest.
  3. Real content collections. Markdown and MDX get typed frontmatter via Zod schemas. Stop me from publishing a post with a missing description. Catch it at astro check, not in production.
// src/content.config.ts
import { defineCollection, z } from 'astro:content';

const posts = defineCollection({
  type: 'content',
  schema: ({ image }) => z.object({
    title: z.string(),
    pubDatetime: z.date(),
    description: z.string(),
    tags: z.array(z.string()).default(['others']),
    featured: z.boolean().optional(),
    ogImage: image().or(z.string()).optional(),
  }),
});

export const collections = { posts };

That schema is the contract. The build fails if I forget a description. The dev server fails if I add a tag that isn’t a string. This is more value than any “headless CMS” I’ve used.

The case for Cloudflare Pages

There are five hosts that can serve a static site well: Cloudflare Pages, Netlify, Vercel, GitHub Pages, AWS Amplify. Of those, Pages is the one I keep choosing for new content sites. Why:

The other hosts are fine. None of them give me the Cloudflare ecosystem behind the same deploy button.

What this site is built with

This blog (jccbbb.com) is the same template, deployed the same way:

Deploy is one command:

npm run build
npx wrangler pages deploy dist --project-name=jccbbb --branch=main

That’s the whole CI step. Build, deploy, done.

The build characteristics

For a content site, build-time work beats request-time work every time. Astro + Pages collapses the runtime to “serve the file.”

A representative Lighthouse run on this blog:

MetricScore
Performance99
Accessibility100
Best Practices100
SEO100
LCP0.8s
CLS0.001
TBT0ms

That’s not a finely tuned setup. That’s the default AstroPaper + Pages, with one blog post on the page.

The reason it’s that fast: there’s no JS. There’s no SSR latency. There’s no client-side router waiting to hydrate. There’s one HTML file, a sliver of CSS, and a few SVGs. The CDN serves it in 30ms from your nearest PoP. Lighthouse can’t find anything to complain about because there isn’t anything happening.

When this stack stops fitting

Three cases where I reach for something else:

  1. The site is mostly an app. Auth, state, dashboards. Move to a real React or Vue setup, probably still on Cloudflare (Pages + a Worker, or a single SSR Worker), but not Astro.
  2. The content lives in a CMS the editors love. If a non-technical team has been editing in WordPress for ten years, headless WP into Astro is fine, but pure markdown isn’t going to fly.
  3. The site needs heavy server-rendered personalisation. Pages can do it via SSR, but at that point you’re shipping a Worker that happens to render HTML. Cut the middleman and design it as a Worker from day one.

Why I keep coming back

The honest answer: Astro + Cloudflare makes shipping a content site boring. No DevOps surprises. No mysterious build errors. No “we’re rate-limited on the CDN.” No “the framework changed its data API.” Boring is the goal.

When the next interesting project lands, the content site doesn’t need attention. That’s how you get to ship the next thing.

For the broader picture of why I run everything on Cloudflare, Why I’m Building Cloudflare-Native Systems. For how the fullstack apps that sit alongside these content sites are structured, How I Structure Fullstack Projects on Cloudflare.


Share this post:

Previous Post
How I Rebuilt Coretours on 100% Cloudflare
Next Post
How I Structure Fullstack Projects on Cloudflare