Skip to content

Deploying a Static Astro Site to Cloudflare Workers

3 min read

The official Cloudflare adapter for Astro (@astrojs/cloudflare) is designed for SSR. If your Astro site is fully static — output: 'static' — you don’t need it. In fact, using it for a static site causes a chicken-and-egg build error.

Here’s the clean approach: Workers static assets.

How it works

Cloudflare Workers supports serving static files directly from a dist/ directory via the assets configuration key. No adapter needed, no SSR middleware, no complexity:

  1. astro build outputs to dist/
  2. wrangler deploy uploads dist/ to Cloudflare’s edge network
  3. Worker requests are handled by the static asset handler automatically

Setup

Install wrangler:

npm install -D wrangler

Create wrangler.jsonc:

{
  "name": "my-site",
  "compatibility_date": "2026-03-01",
  "compatibility_flags": ["nodejs_compat"],
  "assets": {
    "directory": "./dist"
  },
  "observability": {
    "enabled": true
  }
}

That’s the entire configuration. No main field, no Worker script — just the static assets binding.

Add scripts to package.json:

{
  "scripts": {
    "build": "astro build",
    "deploy": "wrangler deploy",
    "preview": "astro build && wrangler dev"
  }
}

GitHub integration

The Cloudflare dashboard has a native GitHub integration that automatically builds and deploys on push. For a static Astro site, configure it as:

  • Build command: npm run build
  • Build output: dist
  • Framework preset: Astro

When you push to main, Cloudflare builds and deploys to production. For pull requests, it creates a preview deployment with a unique URL — great for review.

Preview URLs

Every branch push gets a preview URL in the format:

https://<branch-dashes>-<worker-name>.<subdomain>.workers.dev

For example, a branch feat/dark-mode on a Worker named my-site under user subdomain:

https://feat-dark-mode-my-site.user.workers.dev

Cloudflare posts this URL as a GitHub check run. Extract it programmatically:

gh api repos/OWNER/REPO/commits/$SHA/check-runs \
  --jq '.check_runs[] | select(.name | startswith("Workers")) | .output.summary' \
  | grep "Preview Alias URL"

Caveats

SPA routing: Static assets doesn’t support SPA-style client-side routing without a Worker script. If you need that, add a _worker.js or use the proper adapter.

Edge functions: If you need server-side logic (API routes, auth), you need @astrojs/cloudflare. Static assets is for fully-static sites only.

Custom domains: Configure in the Cloudflare Workers dashboard under Settings → Domains & Routes.

For a personal site or blog — static is almost always the right call. Keep it simple.