Tutorial
How to Deploy an Astro Site to Cloudflare Workers (2026 Guide)
For years, “deploy Astro to Cloudflare” meant Cloudflare Pages. That advice is now stale: Cloudflare has shifted its focus to Workers with static assets, recommends Workers for new projects, and is no longer bringing new features to Pages. The good news is that deploying a static Astro site to Workers is arguably simpler than Pages ever was — one config file, one command, and your site is on Cloudflare’s edge.
This is the exact setup I use for this blog and for my commercial Astro themes. It covers:
- Why Workers (and what “static assets” means)
- The minimal
wrangler.jsoncfor a static Astro site - Deploying with
wrangler deploy - Attaching a custom domain
- Gotchas: 404 pages, trailing slashes, and when you actually need the Cloudflare adapter
Why Workers instead of Pages?
A quick decision table before we start:
| You are building… | Use |
|---|---|
| A new static or SSR Astro site | Workers (this guide) |
| An existing site already on Pages | Pages still works; migrate when convenient |
| A site that needs D1, Durable Objects, cron, or queues | Workers (Pages never got full parity) |
“Static assets” is the Workers feature that made Pages redundant: a Worker can now ship a
directory of files (your dist/) that Cloudflare serves directly from its CDN. For a fully
static site you don’t write or pay for any Worker code at all — requests served from static
assets are free and unmetered, even on the free plan.
Step 0: An Astro site
Any static Astro project works. If you’re starting fresh:
pnpm create astro@latest my-site -- --template minimal
cd my-site
Astro is static by default — no adapter needed. pnpm build outputs plain HTML/CSS/JS to
dist/. That’s our deployable artifact.
Step 1: Install Wrangler
Wrangler is Cloudflare’s CLI. Keep it as a dev dependency so your deploys are reproducible:
pnpm add -D wrangler
Step 2: Write wrangler.jsonc
Create wrangler.jsonc in your project root. This is the entire configuration for a static
site:
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-site",
"compatibility_date": "2026-07-27",
"assets": {
"directory": "./dist",
"not_found_handling": "404-page"
}
}
Three things worth understanding:
namebecomes your Worker’s name and its free preview URL:my-site.<your-subdomain>.workers.dev.assets.directorypoints at Astro’s build output. There is nomainfield — with no Worker script, Cloudflare serves the directory directly.not_found_handling: "404-page"serves yourdist/404.html(Astro builds one fromsrc/pages/404.astro) with a proper 404 status. If you were deploying an SPA instead, you’d use"single-page-application"here.
Step 3: Deploy
Authenticate once, then deploy:
pnpm exec wrangler login
pnpm build
pnpm exec wrangler deploy
Wrangler uploads dist/, and a few seconds later your site is live at
https://my-site.<your-subdomain>.workers.dev. Add a script to package.json so a deploy is
always a fresh build:
{
"scripts": {
"deploy": "astro build && wrangler deploy"
}
}
From now on, shipping is pnpm deploy.
Step 4: Custom domain
If your domain’s DNS is already on Cloudflare (i.e. the zone lives in the same account), you
don’t need to touch the dashboard. Declare the domain in wrangler.jsonc:
{
// …everything from before…
"routes": [
{
"pattern": "blog.example.com",
"custom_domain": true
}
]
}
On the next wrangler deploy, Cloudflare creates the DNS record and provisions the TLS
certificate automatically. The site you’re reading was attached to astro.p4ni.com exactly
this way — one line of config, one deploy.
Don’t forget to tell Astro its production URL so canonical URLs, sitemaps, and RSS links are absolute:
// astro.config.mjs
export default defineConfig({
site: 'https://blog.example.com',
});
Gotchas worth knowing
Trailing slashes. By default, static assets use “auto” HTML handling: /about/ serves
about/index.html and /about 308-redirects to /about/. That matches Astro’s default
directory-style output, so things just work — but keep your internal links consistent (I set
trailingSlash: 'always' in astro.config.mjs so dev and prod behave identically).
_headers and _redirects. The Pages-style _headers and _redirects files work with
Workers static assets too. Drop them in public/ and Astro copies them into dist/. Handy
for cache headers on fonts or redirecting old URLs after a migration.
Preview before deploying. wrangler dev serves the built site locally under the same
asset-serving rules as production — useful for checking redirect/404 behavior that
astro preview approximates slightly differently.
When you do need the adapter. Everything above assumes a fully prerendered site. The
moment you need SSR — personalization, form handling, an API route hitting
D1 — add @astrojs/cloudflare. Your static pages
still ship as free static assets; only server-rendered routes invoke the Worker. That hybrid
setup is how my directory theme Almanac works: static browsing
pages, with D1-backed search, submissions, and an admin panel on the Worker side.
Recap
pnpm add -D wrangler # 1. CLI
# 2. wrangler.jsonc with assets.directory = ./dist
pnpm exec wrangler login # 3. authenticate once
pnpm deploy # 4. astro build && wrangler deploy
Static Astro on Workers gives you free, unmetered hosting on Cloudflare’s edge with a config file short enough to memorize — and a clean upgrade path to D1 and SSR when your site outgrows “just static.”