Skip to content

Delivery: Pages, D1, R2

The dashboard is where content is written. Delivery is where it is read: a static Astro site on Cloudflare.

Service Role
Pages Hosts the compiled Astro site and serves it from the edge cache
D1 The site’s database (content, media metadata, users, roles, taxonomies, terms)
R2 Media files, served through a Worker route or a public base URL
Workers Runtime endpoints for the site: content reads, media upload and serve
DNS Custom domains, all proxied so caching and WAF apply

Everything lives in the client’s Cloudflare account. Each site has its own database, bucket and Pages project, so there is no shared data plane between clients.

D1 content ──► Astro build ──► static output ──► Cloudflare Pages

Astro reads every content record and generates one page per record, plus the index routes. Because the output is static HTML, publishing is a copy operation, not a server render.

A Pages project per site. Each build produces a new deployment with an immutable preview URL; production points at the latest successful build on the main branch. That gives you:

  • a preview URL for every build,
  • instant rollback to a previous deployment,
  • branch previews for review before publishing.

The site’s Worker exposes the small runtime surface the frontend needs:

Route Purpose
GET /content Read content for dynamic widgets and search
POST /media/upload Accept an upload, store it in R2
GET /media/* Serve a file from R2 by key

The Worker verifies the token’s site_id against the site it is deployed for and rejects mismatches with 403 — so a token for one site cannot read another site’s data even if it reaches the wrong Worker.

A media file has two coordinates:

  • the key in R2, e.g. acme/2316-hero.webp
  • the public URL, <public-base>/<key>

Because keys can contain slashes, the serve route is a wildcard (/media/*), not a single-segment path.

A custom domain is a proxied DNS record pointing at Pages. Proxying matters: it is what puts Cloudflare’s cache, TLS and WAF in front of the site.

Static HTML is cached at the edge and revalidated per deployment. Publishing a new build invalidates the affected paths. Long-lived media is cached aggressively — filenames should change when the file changes.