Type something to search...
How to deploy an Astro site to Cloudflare Pages?

How to deploy an Astro site to Cloudflare Pages?

You can still deploy a fully static Astro site to Cloudflare Pages: connect your repository in the Cloudflare dashboard, use npm run build as the build command and dist as the output directory. But for anything new, and for any site with on-demand pages, the answer has changed. The Astro docs note that Cloudflare recommends Cloudflare Workers for new projects, and the @astrojs/cloudflare adapter dropped Pages support entirely in v13, the version that came with Astro 6. The current way to deploy Astro to Cloudflare is to run npx astro add cloudflare, build with astro build, and publish with npx wrangler deploy, or connect your repository with Workers Builds so every push deploys automatically.

If you followed an older tutorial, this is confusing: many guides still say "Cloudflare Pages", and Cloudflare's own Pages guide for Astro hasn't caught up at the time of writing. In this article you'll learn what changed and why, how to deploy a static site and a server-rendered site to Workers, how to set up automatic deploys from Git, how to use environment variables, secrets and bindings, how to handle custom 404 pages and Node.js APIs, and how to move an existing Pages project across.

Pages vs Workers: What Changed

Cloudflare used to have two separate products for websites. Pages hosted static files with Git-based deploys and ran server code through Pages Functions. Workers ran code at the edge. Workers can now serve static assets directly, which gives one product that does both. Cloudflare's migration guide puts it this way: "Unlike Pages, Workers has a distinctly broader set of features available to it", and Cloudflare publishes a guide for moving Pages projects to Workers.

Astro followed. Here's how the current setup differs from older Astro and Pages guides:

Older Astro + Pages setupAstro 6/7 + Workers
Adapter version@astrojs/cloudflare v12 and earlierv13 and later (14.4.2 at the time of writing)
Deploy targetCloudflare PagesCloudflare Workers with static assets
Deploy commandwrangler pages deploy distwrangler deploy
Dev server runtimeNode.jsCloudflare's workerd
Environment and bindingsAstro.locals.runtime.envimport { env } from 'cloudflare:workers'
Execution contextAstro.locals.runtime.ctxAstro.locals.cfContext
Wrangler maindist/_worker.js/index.js@astrojs/cloudflare/entrypoints/server

The adapter's upgrade notes say it directly: "The Astro Cloudflare adapter no longer supports deployment on Cloudflare Pages. For the best experience and feature support, you should migrate to Cloudflare Workers."

So if your Astro site renders anything on demand, uses actions, sessions or server islands, or uses Cloudflare bindings such as KV or D1, deploy it to Workers. If it's purely static, either product works, but Workers is where Cloudflare is putting new features.

Option 1: Deploy a Static Astro Site to Workers

A fully prerendered site doesn't need the adapter. Install Wrangler, Cloudflare's CLI:

npm install wrangler@latest --save-dev

Create a Wrangler config at the project root that points at your build output. There's no main field because there's no Worker code, only assets:

// wrangler.jsonc
{
  "name": "my-astro-site",
  "compatibility_date": "2026-10-19",
  "assets": {
    "directory": "./dist",
    "not_found_handling": "404-page"
  }
}

Set compatibility_date to the day you first deploy. not_found_handling: "404-page" tells Cloudflare to serve your 404.html for unknown URLs. Workers doesn't infer this the way Pages did, so without it you won't see your custom 404 page.

Then build and deploy:

npx astro build
npx wrangler deploy

The first time, Wrangler asks you to log in. A dry run shows what will be uploaded without deploying anything:

npx wrangler deploy --dry-run
✨ Read 3 files from the assets directory .../dist
Total Upload: 0.31 KiB / gzip: 0.22 KiB
No bindings found.
--dry-run: exiting now.

After a real deploy, Wrangler prints a workers.dev URL for your site.

Option 2: Deploy a Server-Rendered Astro Site to Workers

For on-demand pages, add the adapter:

npx astro add cloudflare

In an Astro 7 project, this command does more than add one line of config. It installs @astrojs/cloudflare and wrangler, adds a generate-types script that runs wrangler types, adds worker-configuration.d.ts to your tsconfig.json, and creates a wrangler.jsonc:

// wrangler.jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_date": "2026-10-19",
  "compatibility_flags": ["global_fetch_strictly_public"],
  "name": "astro-sandbox",
  "main": "@astrojs/cloudflare/entrypoints/server",
  "assets": {
    "directory": "./dist",
    "binding": "ASSETS"
  },
  "observability": {
    "enabled": true
  }
}

The name comes from your package.json. Change it to whatever you want the Worker to be called. Your Astro config now has the adapter:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  adapter: cloudflare(),
});

The Wrangler file is optional for simple projects. If you delete it, Astro generates a default configuration using your package name. You need it as soon as you add bindings, variables or other custom settings.

Build, Preview and Deploy

Build as usual:

npx astro build

The adapter splits the output into static files and a Worker, and generates a complete Wrangler config inside dist:

dist/client/index.html
dist/client/_headers
dist/client/.assetsignore
dist/server/entry.mjs
dist/server/wrangler.json

The build log shows what it configured for you:

[@astrojs/cloudflare] Enabling image processing with Cloudflare Images for production with the "IMAGES" Images binding.
[@astrojs/cloudflare] Enabling sessions with Cloudflare KV with the "SESSION" KV binding.
[@astrojs/cloudflare] Injected immutable Cache-Control for /_astro/* into _headers.

Preview the production build locally. astro preview runs it in workerd, the same runtime Cloudflare uses:

npx astro preview

Then deploy:

npx wrangler deploy

Wrangler finds the generated config automatically. A dry run shows the redirection and the bindings your Worker will get:

Using redirected Wrangler configuration.
 - Configuration being used: "dist/server/wrangler.json"
 - Deploy configuration file: ".wrangler/deploy/config.json"
...
Your Worker has access to the following bindings:
Binding             Resource
env.SESSION         KV Namespace
env.IMAGES          Images
env.ASSETS          Assets

The SESSION KV namespace and IMAGES binding are provisioned automatically when you deploy. If you don't use sessions, set session: false in your Astro config so no KV namespace is created. If you'd rather transform images only at build time, set the adapter's imageService option to 'compile'.

Deploying Automatically From Git With Workers Builds

To get the Pages-style "push to deploy" workflow on Workers, use Workers Builds:

  1. Set up the project as in Option 1 or 2 and push it to GitHub or GitLab.
  2. Open the Cloudflare dashboard and go to Compute > Workers & Pages, then select Create application.
  3. Under Import a repository, pick your Git account and repository.
  4. Set the commands: build command npx astro build, deploy command npx wrangler deploy.
  5. Click Save and Deploy. Your Worker is available at its workers.dev subdomain, and you can add a custom domain afterwards.

Deploying a Static Site to Pages Anyway

If you have a static Astro site and prefer Pages, it still works. Pages only needs your built files, so no adapter is involved:

  1. In Workers & Pages, choose Create application, open the Pages tab and choose Import an existing Git repository.
  2. Select the repository, set the production branch, the build command npm run build and the build output directory dist.
  3. Select Save and Deploy.

You can also upload a local build directly with Wrangler:

npx astro build
npx wrangler pages deploy dist

Be careful with Cloudflare's own "Deploy an Astro site" Pages guide: when we checked it, it still described SSR through Pages Functions and bindings through Astro.locals.runtime.env, both of which belong to the older adapter. Don't install the current adapter on a Pages project.

Environment Variables, Secrets and Bindings

On Workers, environment variables, secrets and bindings all live on the Worker's env object. Define non-sensitive variables in your Wrangler config:

// wrangler.jsonc
{
  "vars": {
    "GREETING": "Hello from wrangler.jsonc"
  }
}

Set secrets with Wrangler, so they're encrypted and never stored in your repository:

npx wrangler secret put API_TOKEN

For local development and astro preview, put secrets in a .dev.vars file at the project root, and keep it out of Git:

# .dev.vars
API_TOKEN=local-dev-token

Read them by importing env from cloudflare:workers in any on-demand page, endpoint or middleware:

---
// src/pages/env.astro
export const prerender = false;

import { env } from 'cloudflare:workers';

const country = Astro.request.cf?.country ?? 'unknown';
---
<p>{env.GREETING}</p>
<p>Token set: {env.API_TOKEN ? 'yes' : 'no'}</p>
<p>Country: {country}</p>

Running this with astro preview returned:

<p>Hello from wrangler.jsonc</p><p>Token set: yes</p><p>Country: BD</p>

Astro.request.cf is Cloudflare's request metadata object, which includes geolocation. The same variables also work with Astro's typed astro:env API, which keeps your code portable if you ever change host.

To get TypeScript types for your variables and bindings, run wrangler types after every change to wrangler.jsonc or .dev.vars. It writes worker-configuration.d.ts, which astro add already included in your tsconfig.json:

npx wrangler types

The execution context, for example to call waitUntil() for work that should finish after the response is sent, is available as Astro.locals.cfContext.

Node.js APIs and Dependencies

Workers support many Node.js APIs through the nodejs_compat compatibility flag. Add it if your server code imports modules such as node:buffer or node:crypto:

// wrangler.jsonc
{
  "compatibility_flags": ["nodejs_compat"]
}

Use the node: prefix in imports. If a build fails with a message saying a package "wasn't found on the file system but is built into node", a dependency needs a Node.js API that Workers doesn't provide, and you'll need to check with the package author or replace it.

By default, prerendered pages are also built inside workerd. If a prerendered page reads files with node:fs or uses a package that doesn't run in Workers, set prerenderEnvironment: 'node' in the adapter options. On-demand pages always run in workerd.

Migrating an Existing Pages Project to Workers

  1. Upgrade Astro and @astrojs/cloudflare to current versions.
  2. Update wrangler.jsonc: replace pages_build_output_dir with an assets.directory, and set main to @astrojs/cloudflare/entrypoints/server (or remove the file if you have no custom settings).
  3. Replace Astro.locals.runtime: use import { env } from 'cloudflare:workers' for variables and bindings, Astro.request.cf for request metadata, the global caches object for the Cache API, and Astro.locals.cfContext for the execution context.
  4. Set not_found_handling if you rely on a custom 404 page.
  5. Move variables, secrets and bindings from the Pages project to the Worker, and connect the repository with Workers Builds.
  6. Validate the Worker, move your custom domain across, then delete the Pages project.

If you deploy to several Cloudflare environments, note that the environment is now chosen at build time, so build separately for each one, for example CLOUDFLARE_ENV=staging npx astro build && npx wrangler deploy.


FAQ: Deploying Astro to Cloudflare

Yes for fully static sites, because Pages only needs the built dist folder. For on-demand rendering, actions, sessions or bindings, use Cloudflare Workers, since the current @astrojs/cloudflare adapter no longer supports Pages.

No. A static site only needs a Wrangler config whose assets.directory points to ./dist, then npx wrangler deploy. Add the adapter when you need on-demand rendering or Cloudflare bindings.

It was removed in @astrojs/cloudflare v13 for Astro 6. Import env from cloudflare:workers for variables and bindings, read request metadata from Astro.request.cf, and use Astro.locals.cfContext for the execution context.

Run astro build and then astro preview. The preview runs your Worker in workerd, Cloudflare's runtime, and loads local secrets from .dev.vars. astro dev also uses workerd with the current adapter.

Workers doesn't infer 404 handling the way Pages did. Set assets.not_found_handling to "404-page" in your Wrangler config so the 404.html file from your build is served for unknown URLs.

Cloudflare has a free Workers plan with daily request limits. Its docs say requests to static assets are free and unlimited, while requests that run your Worker, such as on-demand pages, are billed under Workers pricing. Check Cloudflare's current pricing for the limits that apply to your account.


Conclusion

Cloudflare Pages can still host a static Astro build, but the recommended path today is Cloudflare Workers. For a static site, that means a three-line Wrangler config and npx wrangler deploy. For a server-rendered site, npx astro add cloudflare sets up the adapter, Wrangler and types, the build generates the Worker config, and astro preview lets you test in the real workerd runtime before deploying.

If you're coming from an older Pages setup, the main changes are the deploy command, the Wrangler entry point, and how you reach variables and bindings: cloudflare:workers replaces Astro.locals.runtime. Move those across, set not_found_handling for your 404 page, connect Workers Builds for automatic deploys, and you'll be on the setup that both Astro and Cloudflare are actively developing.

Tags :
Share :

Related Posts

What are Astro Actions and how do they work?

What are Astro Actions and how do they work?

Astro Actions are type-safe backend functions that you define once on the server and then call from the browser as if they were ordinary async fu

Dive Deeper
What are Astro adapters and which one should you choose?

What are Astro adapters and which one should you choose?

An Astro adapter is an integration that builds the server part of your project for a specific runtime, such as Node.js, Vercel Functions, Netlify

Dive Deeper
How to build API endpoints in Astro?

How to build API endpoints in Astro?

To build an API endpoint in Astro, create a .js or .ts file in src/pages/ and export a function named after an HTTP method, such as GET or

Dive Deeper