今天把这个 AstroPaper 博客部署到 Cloudflare Pages,本来以为只是连仓库、填构建命令、等构建完成。结果从 Cloudflare 的安装依赖阶段,一路排到本地 adapter、pnpm、Wrangler 和 Astro 静态构建模式。
这篇不是标准教程,而是一次真实排错记录。
1. 第一个失败:packages field missing or empty
Cloudflare Pages 上第一段失败日志是这样的:
Detected the following tools from environment: pnpm@10.11.1, npm@10.9.2, nodejs@22.16.0
Installing project dependencies: pnpm install --frozen-lockfile
ERROR packages field missing or empty
Failed: error occurred while installing tools or dependencies
这个错误不是 Astro 页面写错了,也不是 Cloudflare 没识别项目。真正的问题在 pnpm-workspace.yaml。
项目里原来只有:
allowBuilds:
esbuild: true
sharp: true
但 Cloudflare 用 pnpm install --frozen-lockfile 安装依赖时,pnpm 期望 workspace 文件里有明确的 packages 字段。修复方式是补上根项目:
packages:
- "."
allowBuilds:
esbuild: true
sharp: true
这一步修的是 Cloudflare 安装依赖失败的根因。
2. 第二个坑:照教程加 Cloudflare adapter 反而把问题扩大了
中间看了 Cloudflare 的 Astro 部署教程,然后跑:
npm run astro add cloudflare
命令提示它要执行:
pnpm add @astrojs/cloudflare@^14.1.4 wrangler@^4.112.0
但本机当时直接失败:
Error installing dependencies.
spawn pnpm ENOENT
Astro could not update your astro.config.js file safely.
这里的意思是:Astro CLI 想调用 pnpm,但当前 shell 环境里找不到 pnpm 可执行文件。于是依赖没装完整,配置也没安全更新。
更麻烦的是,后来 astro.config.ts 里已经出现了:
import cloudflare from "@astrojs/cloudflare";
export default defineConfig({
adapter: cloudflare(),
});
但依赖没有成功安装时,再跑同一个命令或启动项目,就会变成:
Unable to load your Astro config
Cannot find module '@astrojs/cloudflare' imported from 'astro.config.ts'
这不是 Astro 神秘坏掉了,而是配置引用了一个不存在的包。依赖安装失败后,不能只看配置文件有没有变,还要确认 package.json、锁文件和 node_modules 三者是否一致。
3. 真正的判断:这个博客应该走静态部署,不该走 Workers runtime
继续加 Cloudflare adapter 后,本地又出现了这个错误:
Failed to load url node:fs
Unexpected Node.js imports for environment "ssr"
Do you need to enable the "nodejs_compat" compatibility flag?
日志里还能看到调用链进入了 Cloudflare adapter:
@astrojs/cloudflare/dist/utils/handler.js
workers/runner-worker/index.js
这个信号很关键:项目已经不再只是普通静态站构建,而是进入了 Cloudflare Workers runtime。Workers runtime 默认不是完整 Node.js 环境,所以遇到 Astro content loader 里的 node:fs、node:path、node:url 这类 Node API 时就会报错。
这个项目当前是静态博客,没有服务端渲染接口,也没有运行在 Workers 上的动态逻辑。Cloudflare Pages 可以直接托管 Astro 构建出来的静态文件,所以最简单、最稳定的方式就是让 Astro 正常输出 dist。
所以最后不是去加 nodejs_compat 掩盖问题,而是移除 Cloudflare adapter:
- import cloudflare from "@astrojs/cloudflare";
- adapter: cloudflare(),
移除后再构建,日志回到正确模式:
[build] output: "static"
[build] mode: "static"
[build] directory: dist/
[build] Complete!
4. .wrangler 是调试产物,不要提交
加 adapter 和 Wrangler 调试后,工作区出现了 .wrangler:
.wrangler/deploy/config.json
.wrangler/state/v3/cache/...
.wrangler/state/v3/kv/...
这些都是 Wrangler/Miniflare 的本地状态,不是源码。它们应该加入 .gitignore:
.wrangler/
否则仓库会混入一堆本机缓存文件,后续排查会更乱。
5. pnpm approve-builds 不要机械照抄提示
期间还遇到一个 pnpm 的提示:
Ignored build scripts: workerd
Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts.
这个提示本身没问题,但不能把“提示文字”当成 workspace 配置写进去。之前 workspace 文件里一度出现过类似这种无效内容:
allowBuilds:
esbuild: true
sharp: true
workerd: set this to true or false
这不是合法配置。后来既然确认项目不需要 Cloudflare adapter,也就不需要 workerd、wrangler、@astrojs/cloudflare 这条依赖链。最小修复是把这些半成品改动清掉,只保留真正需要的 packages 字段和 .wrangler/ 忽略规则。
6. Cloudflare Pages 的最终配置
这个项目部署到 Cloudflare Pages 时,最关键的是这两个配置:
Build command: npm run build
Build output directory: dist
项目里的构建脚本是:
{
"scripts": {
"build": "astro check && astro build && pagefind --site dist && cp -r dist/pagefind public/"
}
}
也就是说,Cloudflare 执行 npm run build 后,真正应该发布的是 dist 目录。不要把输出目录填成 public,也不要因为有 Pagefind 就改成别的目录。
Cloudflare 页面上的配置建议是:
Build command: npm run build
Build output directory: dist
Root directory: /
如果你用的是 Cloudflare Workers 的“部署命令”模式,页面上可能会看到:
Deploy command: npx wrangler deploy
但这个博客不需要 Workers 部署命令。它需要的是 Pages 静态托管 dist。
7. Node 版本要和项目声明一致
package.json 里声明了 Node 要求:
{
"engines": {
"node": ">=22.12.0"
}
}
Cloudflare Pages 默认 Node 版本不一定符合项目要求。版本不一致时,可能不是业务代码错,而是构建环境太旧。
建议在 Cloudflare Pages 的环境变量里显式设置:
NODE_VERSION=22.12.0
这类问题不要用改代码来绕,先让部署环境和项目要求一致。
8. Pagefind 会制造本地脏文件,要提前忽略
这个项目启用了 Pagefind 搜索。构建脚本会先对 dist 建索引,再把索引复制到 public/pagefind:
pagefind --site dist && cp -r dist/pagefind public/
这对运行时搜索是有用的,但它也意味着本地每次构建后都可能出现 public/pagefind 变化。
正确处理方式是忽略它:
public/pagefind/
不要把搜索索引当作手写源码提交。它应该由构建过程生成。
9. 上线前别忘了改 site.url
AstroPaper 的站点地址配置会影响 canonical URL、RSS、站点地图和 OG 图片地址。当前配置位置在:
// astro-paper.config.ts
site: {
url: "https://astro-paper.pages.dev/",
}
正式部署后,这里应该改成自己的 Cloudflare Pages 地址或自定义域名,例如:
site: {
url: "https://your-site.pages.dev/",
}
如果这里忘了改,页面也许能打开,但 SEO、分享卡片、RSS 里的链接都会指向错误域名。这种问题不一定在构建时报错,但上线后影响很实际。
最后确认清单
下次部署 Astro 静态博客到 Cloudflare Pages,可以按这个顺序检查:
pnpm-workspace.yaml里必须有packages字段,至少包含当前根项目。- 先判断项目是不是纯静态站点。如果是,就不要加 Cloudflare adapter。
- 不要把
astro add cloudflare失败后的半成品配置留在仓库里。 - Cloudflare Pages 构建命令填
npm run build。 - 输出目录填
dist。 - Root directory 填
/。 - 设置
NODE_VERSION=22.12.0,和package.json保持一致。 .wrangler/放进.gitignore。public/pagefind/放进.gitignore。- 把
astro-paper.config.ts里的site.url改成真实线上地址。
这次最大的教训是:部署问题不要急着补配置。先判断项目的运行模型。静态站就按静态站部署,只有真的需要 SSR、Workers runtime 或服务端 API 时,再引入 Cloudflare adapter。