Cloudflare Pages 构建缓存陈旧:3 个原因 + 修复路径

本地 npm i 拿到新依赖,CF Pages 还在用旧的——node_modules 缓存粘住了。

本地 npm install 拿到新依赖、本地 build 完全 OK,推到 Cloudflare Pages 后构建日志里依旧用旧版本,或者构建成功但线上看到的还是改之前的产物——这是 Cloudflare Pages 的 build cache 把上一次的 node_modules 复用进来了。CF Pages 默认缓存 node_modules 和构建中间产物来加速 build,但它判断”该不该复用”的逻辑相对粗糙:只看 package-lock.json 内容哈希,遇到 lockfile 没真正同步、Node 版本漂移、或者依赖的 peer 间接更新时就会失准。本文给一条照抄的清理路径。

常见原因

按命中率从高到低排。

1. Build cache 命中旧 node_modules

最常见。改了 package.json 把某个依赖从 ^1.0.0 升到 ^2.0.0,本地 npm install 重新生成 lockfile,但没 commit 新 lockfile 就推上去。CF Pages 看到 lockfile 哈希没变(用的还是旧版),直接复用缓存的 node_modules,跑的还是 v1。

构建日志里能看到:

Restoring build cache
Restored cache from 2026-05-20 (...)
Installing dependencies
up to date in 1s

up to date in 1s 是典型信号——根本没真正 install。

如何判断:本地 git statuspackage-lock.json 是否未提交;CF Pages 日志里 install 阶段秒过没真正下载。

2. package-lock.json 未提交或被 .gitignore

有些项目 .gitignore 里误把 package-lock.json 加进去,或者用 yarn / pnpm 但 commit 了多种 lockfile,CF Pages 选错了那一份。

如何判断cat .gitignore | grep -E "lock|node_modules",确认 lockfile 没被忽略;ls *.lock* package-lock.json 看仓库里实际有哪些 lockfile。

3. Node 版本漂移导致缓存复用错版本

本地 Node 22,CF Pages 默认还是 Node 18(截至 2026 年,默认版本仍偏旧)。某些原生依赖(如 better-sqlite3sharp)在不同 Node 大版本下编译产物不同,缓存里的 binding 不兼容运行时 Node,build 成功但 runtime 崩溃。

如何判断:CF Pages 构建日志第一行看 Node 版本,对比本地 node -v

4. 构建命令没真正重跑

Build command 设的是 npm run build,但 framework preset 还偷偷在前面加了一层缓存判定。或者 BUILD_COMMAND env var 改了但 deploy 还在用项目设置里的旧命令。

如何判断:构建日志顶部看实际执行的命令字符串。

5. 输出目录(dist / .vercel/output)也被缓存

更少见,但如果用了自定义 build script 把上次输出 cp 出来,Pages 会把那个 dir 当作”已构建”直接发布,跳过当前的代码改动。

如何判断:构建日志里 “Uploading” 阶段的文件列表 vs 你本地 dist/ 的内容是否一致。

最短修复路径

Step 1:手动 purge build cache 跑一次干净 deploy

Pages dashboard:

你的项目 → Settings → Builds & deployments → Build cache → Clear cache

清完后立刻点 Deployments → Retry deployment 触发一次完全冷启动的 build。日志里应该看到:

No build cache available
Installing dependencies
added 487 packages in 32s

added N packages in Ns 比上次的 up to date in 1s 慢得多,证明真的重新装了。

Step 2:确认 package-lock.json 已提交且和 package.json 同步

# 本地强制重新生成 lockfile
rm -rf node_modules package-lock.json
npm install

# 确认在 git 里
git status package-lock.json
git add package-lock.json package.json
git commit -m "sync lockfile"
git push

.gitignore 检查:

grep -E "package-lock|yarn.lock|pnpm-lock" .gitignore
# 期望:无输出,或这些行被注释

只保留一种 lockfile。若用 pnpm 就删除 package-lock.jsonyarn.lock,保留 pnpm-lock.yaml

Step 3:在项目里锁死 Node 版本

CF Pages 支持多种方式指定 Node 版本,按优先级:

# 方式 1:项目根 .nvmrc(推荐,本地和 CI 都生效)
echo "22.11.0" > .nvmrc
git add .nvmrc

# 方式 2:package.json engines
{
  "engines": {
    "node": "22.11.0"
  }
}

或在 Pages dashboard 设环境变量:

NODE_VERSION = 22.11.0

设完后下次 build 日志第一行应该显示对应版本,本地和云端一致。

Step 4:把 install 步骤改成显式干净安装

Build command 改成 npm ci && npm run build(不是 npm install && npm run build):

Build command: npm ci && npm run build
Build output directory: dist

npm ci 强制按 lockfile 精确安装,遇到 lockfile 不同步直接 fail,把”复用旧依赖”路径堵死。

Step 5:验证产物和缓存行为

# 本地 build 一次
npm ci && npm run build
ls -la dist/

# 部署后用 curl 抓一个静态资源,看是不是新版本
curl -s https://your-project.pages.dev/_app/immutable/chunks/main.HASH.js | head -c 200

如果新部署上的 hash 文件名变了,说明产物真的更新。如果文件名一样,可能是输出目录被复用,回到 Step 1 再清一次。

预防建议

  • 每次升级关键依赖(特别是带原生 binding 的)后手动在 dashboard purge 一次 build cache
  • Build command 固定用 npm ci,不用 npm install,杜绝 lockfile 漂移
  • .nvmrcengines.node 双重锁定 Node 版本,避免 CF Pages 默认 Node 升级影响构建
  • 项目仓库只保留一种 lockfile(npm / yarn / pnpm 选一种)
  • PR 检查里加一步 “lockfile is up to date” 校验:npm install --package-lock-only && git diff --exit-code package-lock.json

相关阅读

标签: #部署 / 托管 #排查