p4ni.

Comparison

Choosing a CMS for Astro on Cloudflare: Git-Based, Headless, or None at All

· 7 min read

On this page

Search for “cloudflare astro cms” and you get lists: fifteen headless CMSes, each with a logo, a pricing table, and a verdict of “it depends”. What those lists skip is the question that actually decides the choice: on a static Astro site, where does content live, and what triggers the rebuild when it changes?

This blog runs on the stack in the search query — Astro, statically prerendered, served from Cloudflare Workers static assets. It has no CMS at all, and that’s a deliberate choice I’ll defend below. My directory theme Almanac runs on the same stack and made the opposite call for part of its content, backing listings with D1. Between those two poles sit the git-based and hosted headless options everyone actually debates. Here’s the comparison I’d want if I were choosing today.

The short version

Your situationUse
You (a developer) write all the contentNo CMS — content collections in git
Developers write, but need a nicer editor or occasional non-dev editsGit-based CMS (Keystatic, Decap/Sveltia, Tina)
Non-technical editors own the content, or many people editHosted headless CMS (Sanity, Contentful, Storyblok)
The “content” is really data — listings, entries, submissionsD1 + your own admin, or hybrid static/dynamic

Every row above ends in the same place: a folder of prerendered files in dist/, served free from Cloudflare’s CDN. The CMS question is purely about the input side.

The constraint that shapes everything

A static Astro site on Workers has no server rendering pages at request time. Content becomes HTML exactly once, at build time. That means a CMS for this stack isn’t “software that serves your content” — it’s “software that feeds your build”. Two consequences follow.

First, every option needs a build trigger. Content changed → something must run astro build and redeploy. With content in git, that’s your normal CI. With content in a hosted CMS, that’s a webhook pointed at a deploy hook. Neither is hard, but if you forget to wire it up, your site silently serves stale pages forever — the classic headless-CMS gotcha.

Second, the CMS never touches production. Whatever you pick, it can be slow, down, or rate-limited and your live site doesn’t care; it only matters at build time. This is the great luxury of the static model, and it’s why I’d resist any option that reintroduces a runtime dependency for content that doesn’t need one.

If you’re still weighing how to host the output side, I’ve compared Pages and Workers for static sites separately — short version: new projects belong on Workers.

Option 1: No CMS — content collections in git

Astro ships its own content layer, and for a developer-authored site it’s honestly hard to beat. Posts are MDX files in the repo. A schema in src/content.config.ts type-checks every frontmatter field at build time — put a typo in a date and the build fails instead of the page rendering wrong. Components work inside articles. Review happens in pull requests, history is git log, backup is the remote, and search is grep.

The features people assume require a CMS mostly don’t:

  • Scheduled publishing — a frontmatter date, a build-time filter, and a daily deploy. I’ve written up the whole pattern; it’s a cron trigger and four lines of code.
  • Multi-language content — locale subfolders inside the collection, with slugs pairing translations. Again, covered in detail here — no plugin, no CMS localization add-on.
  • Drafts — a draft: true field and the same build-time filter.

The honest limit is the authoring experience. Writing means editing files and pushing commits. For me that’s a feature — my editor, my formatting tools, my git workflow. For a client who writes in Word, it’s a dealbreaker, and no amount of “Markdown is easy” evangelism changes that. Be truthful with yourself about who will actually touch the content in year two.

Option 2: Git-based CMS — an editing UI over the same files

Git-based CMSes keep everything from option 1 — content in the repo, builds from CI, no runtime dependency — and add a browser-based editing UI that commits on the writer’s behalf. The files don’t move; the workflow does.

Keystatic is the one I’d evaluate first for Astro specifically. It’s TypeScript-first, its config mirrors the shape of a content collection schema, and it can run locally against the filesystem — you can adopt it as a nicer editor for existing MDX without any hosted component at all. Decap (the artist formerly known as Netlify CMS) is the veteran: a single config file, mature widget ecosystem, and its age shows mostly in the UI. Sveltia is a modern Decap-compatible rewrite — same config format, dramatically lighter — which makes it a low-risk swap if you start with Decap conventions. Tina goes furthest toward a “real” CMS feel, with visual editing, at the cost of a backend for auth and its own data layer — Tina Cloud hosted, or self-hosted if you bring your own auth and database.

The Cloudflare-specific wrinkle is authentication. Decap and Sveltia need an OAuth flow to commit to GitHub on the editor’s behalf, and the historical answer — “use Netlify’s OAuth service” — is obviously silly if you host on Cloudflare. The good news: the OAuth proxy is a tiny piece of code, and a Worker is a perfect place for it. Several maintained decap-proxy-style Workers exist, or you write your own in an afternoon. It’s one more moving part, but it runs on the platform you’re already on, for free.

Who this fits: a developer-run site where someone else — a co-author, a client, future you on an iPad — occasionally needs to edit without cloning the repo. If nobody fits that description, the UI is pure overhead; stay on option 1 until someone does.

Option 3: Hosted headless CMS — content behind an API

Sanity, Contentful, Storyblok and friends store content on their servers, give editors a real authoring application, and hand your build the content over an API. Astro’s Content Layer makes the integration clean: a loader pulls external content into the same collections API you’d use for local files, so templates don’t care where entries came from. Publish fires a webhook, the webhook hits a deploy hook, Cloudflare rebuilds. (If you build through Workers Builds’ git integration, note the trigger is a commit — content-only changes need the deploy hook wired up, or your CMS edits never ship.)

What you’re buying, concretely: roles and permissions, editorial workflow (draft → review → publish), asset management with image pipelines, localization tooling, and structured content reusable beyond one website. For a marketing site with a content team, that list justifies the platform. Storyblok’s visual editor in particular demos extremely well with clients.

What you’re paying, beyond the subscription: your content now lives in someone else’s database, in someone else’s schema format. Free tiers are fine for a blog and evaporate the moment you add seats or locales — the jump from free to the first paid tier is routinely an order of magnitude more than the Cloudflare bill for the site itself. Migrations out are possible and unpleasant. And your build gains a network dependency: a CMS API outage doesn’t take your site down, but it does block deploys until it passes.

The trap worth naming: solo developers picking option 3 for an audience of one. If you are both the only writer and the only editor, you’ve added a subscription, a schema, and a webhook to avoid writing Markdown in the editor you already own. The structured-content pitch is real, but it’s for teams.

Option 4: When the content is actually data

Some “CMS” problems aren’t content problems. A directory of 500 listings with categories, search, and user submissions doesn’t want to be 500 MDX files — it wants a database. That’s how Almanac works: browsing pages are statically prerendered like any Astro site, while search and submissions hit D1 through a thin Worker route on the same deployment. Cloudflare’s platform makes the hybrid unusually cheap — the static half serves free from assets, and the dynamic half is a few lines away, no second host required. I’ve written up how that architecture fits together on the Almanac blog.

The test: if you find yourself designing frontmatter fields that are really foreign keys, or wishing for a query language over your Markdown, you’ve crossed from content into data. Cross the architecture over too.

How this site decided

One writer, two locales, everything authored in a code editor. Option 1 with no regrets: content collections, MDX, deployed by wrangler from CI, with a daily cron deploy handling scheduled posts. The upgrade paths all stay open — Keystatic could bolt onto these exact files tomorrow, and if a future project brings an editorial team, a Content Layer loader can feed these same templates from a hosted API.

That’s the real answer to “which CMS for Astro on Cloudflare”: start from who edits, not from feature lists. Content collections until that hurts; a git-based UI when a second pair of hands shows up; a hosted platform when a team does; a database when your content stops being prose. The stack underneath — static output on Workers — doesn’t just tolerate all four; it lets you change your mind without replatforming.