Deploy to Cloudflare
Running on Cloudflare Workers with static assets, workerd for on-demand routes and Node compatibility for Better Auth.
The Cloudflare adapter builds a Worker that serves static assets from the edge and runs on-demand routes in the workerd runtime.
-
Create a Workers project connected to the repository (Workers & Pages → Create → Import a repository), or deploy from your machine:
pnpm build:cloudflare pnpm exec wrangler deployWorkers Builds set
WORKERS_CI=1, which selects the adapter automatically. -
Set secrets with Wrangler (they are not stored in
wrangler.jsonc):pnpm exec wrangler secret put DATABASE_URL pnpm exec wrangler secret put DATABASE_AUTH_TOKEN pnpm exec wrangler secret put BETTER_AUTH_SECRET pnpm exec wrangler secret put BETTER_AUTH_URLNon-secret values such as
SITE_URLcan live inwrangler.jsoncundervars;SITE_URLis also needed at build time, so set it in the Workers Builds environment as well. -
Create the tables: add
DATABASE_URLandDATABASE_AUTH_TOKENas GitHub Actions secrets and run the Migrate database workflow (see the database guide). Wrangler secrets are not visible to GitHub Actions. Until the workflow has run, sign-up and the contact form fail because the tables do not exist. -
Deploy. The first deploy provisions the Images binding used for image optimisation.
wrangler.jsonc#
{
"name": "strata-stack",
"compatibility_date": "2025-09-01",
"compatibility_flags": ["nodejs_compat"],
"vars": { "NODE_ENV": "production" },
"observability": { "enabled": true },
}
nodejs_compat is required: Better Auth uses AsyncLocalStorage, and with a compatibility
date of 2025-04-01 or later Cloudflare also populates process.env from your variables and
secrets, which is how src/lib/env.ts reads configuration. NODE_ENV=production turns on the
production safety rules (see security);
.dev.vars overrides it locally. The adapter generates the remaining settings (entrypoint,
assets directory) itself.
What the adapter configures#
cloudflare({ prerenderEnvironment: 'node' });
Prerendered pages and build-time endpoints (Open Graph images use the native resvg module) are
built in Node. On-demand routes always run in workerd, so keep Node-only packages out of
src/pages/api, src/actions and src/middleware.ts. The libSQL client automatically uses its
HTTP build on workerd.
Headers#
Cloudflare has no static-headers option, so the CSP for prerendered pages is injected as a
<meta> element. Additional headers for static assets come from public/_headers; on-demand
responses get them from src/middleware.ts.
Local development#
pnpm dev:cloudflare runs astro dev inside workerd, which cannot open file: databases.
Start a local libSQL server and point DATABASE_URL at it in .dev.vars:
turso dev --db-file .data/local.db # http://127.0.0.1:8080
cp .dev.vars.example .dev.vars
pnpm dev:cloudflare
Regular pnpm dev (Node) remains the fastest way to work on the site day to day.