Skip to main content
The Astro frontend deploys to Cloudflare Pages via native git integration. Every push triggers a build; the branch name determines which environment is targeted.

Deployment environments

Production

Branch: mainStatic output (output: "static"), Visual Editing OFF. All public pages are pre-rendered to static HTML at build time and served from Cloudflare’s edge CDN with zero Worker invocations.

Preview / Staging

Branch: any branch other than mainSSR enabled via @astrojs/cloudflare adapter, Visual Editing ON with draft content from Sanity. Each branch gets a unique preview URL: branch-name.ywcc-capstone.pages.dev.
The preview branch acts as the staging environment. It deploys to preview.ywcc-capstone.pages.dev with SSR and Visual Editing enabled so editors can review draft content before it ships.

How it works

Content rebuilds use a Cloudflare deploy hook — the simplest possible pipeline with no GitHub Actions in the loop.
TriggerResult
Push to main (or merged PR)Production deploy to ywcc-capstone.pages.dev
Push to any other branchPreview deploy to branch.ywcc-capstone.pages.dev
Sanity content publishedDeploy hook fires → production rebuild

Build configuration

SettingValue
Framework presetAstro
Build commandnpm install --prefer-offline --no-audit --no-fund && npm run build --workspace=astro-app
Build output directoryastro-app/dist
Root directory/ (monorepo root — npm workspaces require the root package.json)
Root directory must be /, not astro-app/. The build command uses npm workspaces which requires the monorepo root package.json to resolve all workspace packages.

Create the Pages project

1

Connect the repository

Go to dash.cloudflare.comWorkers & PagesCreatePagesConnect to Git → select the astro-shadcn-sanity repo.
2

Configure the build

Set the project name to ywcc-capstone, build command, and output directory as shown in the table above.
3

Set environment variables

Add all required environment variables (see section below) before saving. Variables set here are available at build time and runtime.
4

Deploy

Click Save and Deploy. The first build will run immediately. Subsequent builds trigger automatically on every git push.

Environment variables

Set these in CF Pages → ywcc-capstone → Settings → Environment variables for both Production and Preview environments.

Public variables (plaintext)

VariableValueDescription
PUBLIC_SANITY_STUDIO_PROJECT_ID49nk9b0wSanity project ID
PUBLIC_SANITY_DATASETproductionSanity dataset to query at build time
PUBLIC_SITE_IDcapstoneUsed for GROQ filtering via getSiteParams()
PUBLIC_SITE_THEMEredSets data-site-theme on <html> for CSS overrides
PUBLIC_SITE_URLhttps://ywcc-capstone.pages.devCanonical site URL
PUBLIC_SANITY_STUDIO_URLStudio URLURL of the deployed Sanity Studio
PUBLIC_GTM_IDGTM container IDGoogle Tag Manager container (baked in at build time)
PUBLIC_SANITY_VISUAL_EDITING_ENABLEDtrueEnables Visual Editing overlay
PUBLIC_TURNSTILE_SITE_KEYTurnstile site keyCloudflare Turnstile widget (client-side, baked at build)
SKIP_DEPENDENCY_INSTALLtrueSkips CF Pages automatic npm clean-install, uses build cache
NODE_VERSION24.13.1Pinned Node.js version for build cache reuse

Secrets (encrypted)

VariableDescription
CF_ACCESS_AUDAccess Application Audience tag (64-char hex) — used for JWT verification
TURNSTILE_SECRET_KEYTurnstile server-side verification secret
SANITY_API_WRITE_TOKENSanity write token for form submissions (Editor role)
DISCORD_WEBHOOK_URLDiscord webhook URL for form submission notifications

GitHub Actions secrets (CI only)

SecretDescription
CLOUDFLARE_API_TOKENAPI token with Cloudflare Pages: Edit permission
CLOUDFLARE_ACCOUNT_IDCloudflare account ID
SANITY_API_READ_TOKENSanity read token for build-time content fetching

Multi-project deployment

The same Astro codebase deploys as three independent CF Pages projects, each with different environment variables:
CF Pages ProjectDatasetSite IDThemeURL
ywcc-capstoneproductioncapstoneredywcc-capstone.pages.dev
rwc-usrwcrwc-usbluerwc-us.pages.dev
rwc-intlrwcrwc-intlgreenrwc-intl.pages.dev
All three share the same repo, build command, and output directory. Only the environment variables differ. No code branching per site — PUBLIC_SITE_ID controls GROQ filtering and PUBLIC_SITE_THEME controls the CSS theme.

Build caching

Cloudflare Pages caches build outputs across deployments. The SKIP_DEPENDENCY_INSTALL=true environment variable tells the CF Pages v3 build image to skip the automatic npm clean-install step (which would delete and reinstall all packages from scratch every build).
ScenarioBuild Time
Cold start (no cache)~3m 24s
Warm (with build cache)~2m
Fully cached node_modules~45-75s
What gets cached:
CacheDirectoryEffect
npm global cache.npmnpm install pulls packages from local cache
Astro build cachenode_modules/.astroIncremental Astro builds
Build cache expires after 7 days without a build. If node_modules is cold and SKIP_DEPENDENCY_INSTALL=true, the build may fail. Temporarily remove the variable to force a full install and repopulate the cache, then re-add it.

Build watch paths

Configured per project to avoid unnecessary rebuilds:
  • Include: astro-app/*, package-lock.json
  • Exclude: studio/*, _templates/*, docs/*, tests/*, .github/*, CHANGELOG.md, README.md
Schema changes in studio/ don’t trigger builds directly. Running npm run typegen updates astro-app/src/sanity.types.ts, which is in the watch path.

Set up the deploy hook

1

Create the hook

CF Pages → ywcc-capstoneSettingsBuilds & deploymentsDeploy hooksAdd deploy hook.Name: Sanity Content Publish, Branch: mainSave. Copy the generated URL — treat it as a secret.
2

Configure the Sanity webhook

sanity.io/manage → your project → APIWebhooks → create or edit the webhook:
FieldValue
URLThe deploy hook URL from above
Trigger onCreate, Update, Delete
Filter_type in ["page", "siteSettings", "sponsor", "project", "team", "event"]
DraftsOFF (only fire on publish, not draft saves)
HTTP methodPOST
3

Verify

Publish a content change in Sanity Studio. Check sanity.io/manage → Webhooks → Attempts tab for a 200 OK response, then check CF Pages dashboard for a new production build.

Security headers

astro-app/public/_headers applies these response headers on all routes via Cloudflare Pages CDN:
/*
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()
  Content-Security-Policy: frame-ancestors 'self' https://*.sanity.studio https://*.sanity.io
These headers are only active on deployed Cloudflare Pages. They do not apply in local wrangler pages dev or astro dev.

Local Wrangler preview

To preview the production build locally using Miniflare:
npm run build --workspace=astro-app
cd astro-app && npx wrangler pages dev dist/
Runs on http://localhost:8788.

Troubleshooting

ProblemFix
Build fails: “Invalid binding SESSIONInformational warning from the Cloudflare adapter. Does not affect static builds — safe to ignore.
_headers not showing in responseVerify astro-app/public/_headers exists and dist/_headers is present after build. Only active on deployed CF Pages.
Build slow (~3+ minutes)Ensure SKIP_DEPENDENCY_INSTALL=true is set in both Production and Preview environment variables.
Webhook shows 403Deploy hook URL is wrong or was deleted. Recreate in CF Pages dashboard and update the Sanity webhook URL.
Webhook shows 200 but no build appearsConfirm the deploy hook targets the main branch and CF Pages has git integration active.
Content stale after publishBrief CDN propagation delay. Wait 30 seconds and hard-refresh.

Build docs developers (and LLMs) love