Skip to content

StaticSite

Source: src/Cloudflare/Website/StaticSite.ts

A Cloudflare Worker that serves static assets built by a shell command.

StaticSite runs a build command (e.g. npm run build), content-hashes the output directory, and deploys the result as a Cloudflare Worker with static assets. Use this when your site has its own build step that produces a directory of files — Hugo, Zola, Eleventy, or any custom pipeline.

For Vite-based projects, prefer Cloudflare.Vite which handles building automatically.

Point command at your build script, outdir at where it writes output, and main at a Worker entrypoint that serves the assets. Alchemy runs the command, hashes the output, and deploys the Worker bound to the built assets.

The Worker receives an ASSETS binding it can delegate to. A minimal passthrough Worker looks like:

src/worker.ts
export default {
fetch: (request: Request, env: { ASSETS: Fetcher }) =>
env.ASSETS.fetch(request),
};
const site = yield* Cloudflare.StaticSite("Blog", {
command: "hugo --minify",
outdir: "public",
main: "./src/worker.ts",
});

Use assetsConfig to control how Cloudflare handles routing for your static files — HTML handling, not-found behavior, etc.

const site = yield* Cloudflare.StaticSite("App", {
command: "npm run build",
outdir: "dist",
main: "./src/worker.ts",
assetsConfig: {
htmlHandling: "auto-trailing-slash",
notFoundHandling: "single-page-application",
},
});

Set cwd to run the build command in a subdirectory (e.g. a monorepo package). outdir is resolved relative to cwd.

const site = yield* Cloudflare.StaticSite("Web", {
cwd: "apps/web",
command: "npm run build",
outdir: "dist",
main: "apps/web/worker.ts",
});

By default, all non-gitignored files are hashed to decide whether the build should re-run. Use memo to narrow the scope.

const site = yield* Cloudflare.StaticSite("Docs", {
command: "npm run build",
outdir: "dist",
main: "./src/worker.ts",
memo: {
include: ["content/**", "templates/**", "config.toml"],
},
});