Background
I wanted to host a lightweight personal blog built with Astro. The blog source code was already stored in a GitHub repository, and my goal was simple:
- keep the source code in GitHub
- let Cloudflare build the site automatically
- avoid uploading local build files manually
- use a clean
pages.devURL instead of aworkers.devURL - keep the repository clean by not committing
node_modulesor generated build output
At first, I deployed the site as a Cloudflare Worker. It worked, but the default URL looked like this:
https://victorbi.spearfishingchina.workers.dev/
That was not what I wanted for a simple blog. I wanted a Cloudflare Pages project instead, because the default Pages URL is cleaner:
https://victorbi.pages.dev/
My Project Structure
My Astro project was inside the src directory of the repository.
The important part looked like this:
repo-root/
├── README.md
├── deploy/
│ └── cloudflare/
│ └── website/
│ └── ...
└── src/
├── package.json
├── pnpm-lock.yaml
├── astro.config.ts
├── public/
└── src/
├── content/
├── pages/
├── layouts/
└── components/
The deploy/cloudflare/website/ directory contained already-built static files, such as:
index.html
404.html
_astro/
pagefind/
rss.xml
sitemap-index.xml
That directory was useful for testing, but it was not the right long-term source for Cloudflare Pages. The better approach is to let Cloudflare build the site directly from the Astro source code.
Why I Changed the Deployment Approach
There are two possible ways to deploy a static Astro blog:
Option 1: Build locally and upload the static files
This means running the build on my own machine and uploading the generated files.
For example:
cd src
pnpm run build
Then I would upload the generated dist folder manually.
This works, but it is not ideal because every update requires manual steps.
Option 2: Let Cloudflare build from GitHub
This is the better long-term setup.
The workflow becomes:
Write blog post
Commit to GitHub
Push to GitHub
Cloudflare Pages builds the Astro site
Cloudflare Pages deploys the generated dist folder
This is cleaner because GitHub stores the source code, and Cloudflare handles the build and deployment automatically.
Clean Up the Repository
The repository should not track generated files or local dependencies.
I added or updated the root .gitignore file:
node_modules/
src/node_modules/
dist/
src/dist/
deploy/cloudflare/website/
.wrangler/
.dev.vars
.env
Then I removed generated files from Git tracking without deleting them locally:
git rm -r --cached src/node_modules || true
git rm -r --cached deploy/cloudflare/website || true
git rm -r --cached src/dist || true
git rm -r --cached dist || true
After that, I committed the cleanup:
git add .gitignore
git commit -m "Clean up generated files for Cloudflare Pages deployment"
git push
Check the Astro Build Script
Inside src/package.json, I confirmed there was a build script.
The important part is:
{
"scripts": {
"build": "astro build"
}
}
For my project, Cloudflare Pages only needs to run the build command. Astro will generate the static site into the dist directory by default.
Test the Build Locally
Before configuring Cloudflare, I tested the build locally.
cd src
pnpm install
pnpm run build
After a successful build, Astro generated:
src/dist/
That dist folder is the final static website.
It contains files such as:
index.html
404.html
_astro/
posts/
tags/
rss.xml
sitemap-index.xml
Finding the Cloudflare Pages Option
In the Cloudflare dashboard, I went to:
Workers & Pages
→ Create application
The confusing part was that the main screen showed Worker options first, such as:
Continue with GitHub
Connect GitLab
Start with Hello World!
Select a template
Upload your static files
Those options create a Worker application.
For Pages, the correct link was at the bottom:
Looking to deploy Pages? Get started
I clicked Get started there.
Create the Cloudflare Pages Project
After entering the Pages setup, I selected:
Import an existing Git repository
Then I connected my GitHub repository.
Cloudflare Pages Build Settings
I used the following Pages configuration:
Project name:
victorbi
Production branch:
main
Framework preset:
Astro
Root directory:
src
Build command:
pnpm run build
Build output directory:
dist
The important detail is this:
Root directory = src
Build output directory = dist
Because my Astro project lives inside the repository’s src folder, Cloudflare must run the build from there.
So Cloudflare sees the project like this:
repo-root/src/package.json
repo-root/src/astro.config.ts
repo-root/src/pnpm-lock.yaml
After the build, Astro outputs the website here:
repo-root/src/dist
From Cloudflare Pages’ point of view, because the root directory is already src, the output directory is simply:
dist
Not:
src/dist
Set the Node Version
I also added a build environment variable:
NODE_VERSION = 22
This keeps the build environment stable and avoids Node version problems with modern Astro versions.
Deploy the Site
After saving the Cloudflare Pages configuration, I triggered the first deployment.
The expected process is:
Cloudflare pulls the GitHub repository
Cloudflare enters the src directory
Cloudflare installs dependencies
Cloudflare runs pnpm run build
Astro generates the dist folder
Cloudflare Pages deploys the dist folder
After deployment, the site is available on a Pages URL like:
https://victorbi.pages.dev/
Worker vs Pages
I originally created a Worker deployment, and it produced a URL like this:
https://victorbi.spearfishingchina.workers.dev/
That happened because Workers use this default URL format:
worker-name.account-subdomain.workers.dev
For a blog, that was not what I wanted.
Cloudflare Workers can host static sites, but for a simple Astro blog, Cloudflare Pages feels more natural. Pages gives a cleaner default domain and a simpler mental model:
GitHub repository
→ Cloudflare Pages build
→ Static website
Final Setup
My final setup is:
Source code:
GitHub
Hosting:
Cloudflare Pages
Framework:
Astro
Package manager:
pnpm
Project root:
src
Build command:
pnpm run build
Output directory:
dist
Node version:
22
Final Workflow
Now the publishing workflow is simple:
cd src
pnpm run build
If the local build works, I write or edit a blog post, then push the change:
git add .
git commit -m "Add new blog post"
git push
Cloudflare Pages automatically builds and deploys the updated blog.
Notes for Future Me
Do not commit these folders:
node_modules/
src/node_modules/
dist/
src/dist/
deploy/cloudflare/website/
Do not use the old local build output folder as the Cloudflare project root.
The correct Cloudflare Pages project root is:
src
The correct Cloudflare Pages build output directory is:
dist
That means Cloudflare builds from source and deploys the generated Astro static site automatically.