Skip to content

Astro

Source: src/Cloudflare/Website/Astro.ts

A Cloudflare Worker deployed from an Astro project.

Astro runs Astro’s programmatic build with a wrangler-free Cloudflare adapter (@distilled.cloud/astro): server-rendered pages execute in the Worker, prerendered pages and client assets deploy as static assets — no astro.config.*, adapter setup, or Wrangler configuration required.

Input files are content-hashed (respecting .gitignore by default) so unchanged projects skip the build and deploy entirely.

The @distilled.cloud/astro package must be installed in your project (it is loaded dynamically at deploy time):

Terminal window
bun add -d @distilled.cloud/astro

A single call builds the project and deploys the server bundle plus static assets. Pages are server-rendered by default; pages that export const prerender = true are served as static assets. Astro’s server runtime is built against Node APIs, so nodejs_compat is always included in the Worker’s compatibility flags.

const site = yield* Cloudflare.Website.Astro("Website");

With astro: { output: "static" } every page is prerendered at build time and the deploy is assets-only: no server bundle is uploaded — Cloudflare’s asset layer answers every request (including the built 404.html via assets.notFoundHandling: "404-page"). Session provisioning is skipped for declared-static sites since no Worker code runs at request time.

const site = yield* Cloudflare.Website.Astro("Docs", {
astro: { output: "static" },
assets: { notFoundHandling: "404-page" },
});

Bind resources through env like any other Worker. Astro code reads them via import { env } from "cloudflare:workers" (or Astro.locals.runtime.env).

const kv = yield* Cloudflare.KV.Namespace("Cache");
const bucket = yield* Cloudflare.R2.Bucket("Uploads");
const site = yield* Cloudflare.Website.Astro("Website", {
env: {
CACHE: kv,
UPLOADS: bucket,
},
});

Astro’s session API is backed by a KV namespace. One is provisioned and bound under the session binding name (SESSION by default) automatically, so Astro.session works with zero configuration. Bind your own namespace under that name to use it instead, or set sessionKVBindingName: false to opt out of session provisioning.

Bringing your own session namespace

const sessions = yield* Cloudflare.KV.Namespace("Sessions");
const site = yield* Cloudflare.Website.Astro("Website", {
env: {
SESSION: sessions,
},
});

Opting out of session provisioning

const site = yield* Cloudflare.Website.Astro("Website", {
sessionKVBindingName: false,
});

By default, every non-gitignored file is hashed to decide whether a rebuild is needed. Use memo to narrow the scope when your project has large directories that don’t affect the build output.

const site = yield* Cloudflare.Website.Astro("Docs", {
memo: {
include: ["src/**", "public/**", "package.json"],
},
});

The integration is fully programmatic — your astro.config.* file is not read. Common serializable options are exposed under astro.

const site = yield* Cloudflare.Website.Astro("Blog", {
astro: {
site: "https://blog.example.com",
srcDir: "./app",
},
});

Calling Astro with no arguments returns a constructor you can extend to declare the Worker as a named class. The class is both an Effect you can yield* to deploy and a type you can reference elsewhere — useful when other resources need to bind to this Worker.

class Website extends Cloudflare.Website.Astro<Website>()("Website") {}
const site = yield* Website;