Skip to content

VitePress 部署到 Cloudflare Pages:wrangler 直传与 Preview / Production 分支

仲灏2026-06-29约 1 分钟

<div style="display: none;" hidden="true" aria-hidden="true" data-nosnippet>Are you an LLM? You can read better optimized documentation at /pages/20260629180000.md for this page in Markdown format</div>

背景

个人知识库用 VitePress 2 构建,Markdown 与主题配置在源码仓维护,构建产物落在 docs/.vitepress/dist。早期走 GitHub Pagesdeploy.shdist force push 到公开 Pages 仓),后来希望:

  • 全球 CDN 加速、自定义域名在 DNS 侧统一管理;
  • 本地或 CI 一条命令上传静态文件,不必每次 git push 到 Pages 仓;
  • 功能分支先上 Preview,合并生产分支后再切 Production

本文记录用 Cloudflare Pages + wrangler CLI 直传 dist 的完整流程,以及「部署成功却是 Preview 地址」的分支机制说明。示例域名、项目名均已脱敏,可按自己的环境替换。

整体链路

text
源码仓(Markdown + VitePress 配置 + 主题包)
        │  pnpm run build

docs/.vitepress/dist/   ← 纯静态 HTML / JS / CSS
        │  wrangler pages deploy

Cloudflare Pages 项目(例如 my-blog)
        │  自定义域名 CNAME

https://blog.example.com

与 GitHub Pages 的差异:Pages 仓可以不再作为发布媒介,wrangler 直接把目录上传到 Cloudflare;源码仓仍可保持私有。

前置条件

说明
Cloudflare 账号已添加站点域名,或在 Pages 里绑定自定义域
Node.js + pnpm与本地 VitePress 构建一致
wrangler项目 devDependencies 安装,或 npx wrangler
API Token 或 wrangler login非交互环境(CI、脚本)必须用 Token

仓库根目录建议保留 wrangler.toml,与构建输出目录对齐:

toml
name = "my-blog"
pages_build_output_dir = "docs/.vitepress/dist"
compatibility_date = "2024-09-23"

name 为默认项目标识;实际部署时可用环境变量覆盖项目名(见下文脚本)。

创建 API Token(Dashboard)

本地脚本、CI 里无法弹出浏览器登录时,需要在 Cloudflare 控制台创建 API Token

  1. 打开 Cloudflare Dashboard → 右上角头像 → My ProfileAPI Tokens
  2. Create Token → 可选用模板 Edit Cloudflare Workers(含 Pages 上传权限),或自定义 Token。
  3. 自定义时至少勾选:
    • AccountCloudflare PagesEdit
    • 若项目绑在指定账号下,把 Account Resources 限定到该账号。
  4. 创建后只显示一次完整 Token,复制保存;勿写入 Git。

使用方式(当前 shell 有效):

bash
export CLOUDFLARE_API_TOKEN="你的Token"

CI 里放进密钥库(GitHub Actions secrets、Jenkins credentials 等),变量名保持一致即可。

验证(可选):

bash
npx wrangler whoami

本地构建与首次部署

bash
pnpm install
pnpm run build

确认 docs/.vitepress/dist/index.html 存在后:

bash
export CLOUDFLARE_API_TOKEN="${CLOUDFLARE_API_TOKEN}"
npx wrangler pages deploy docs/.vitepress/dist \
  --project-name=my-blog
  • 项目不存在时,wrangler 会提示创建(需 Token 有 Pages Edit 权限)。
  • 成功后终端会输出 Preview URL,形如 https://<hash>.my-blog.pages.dev

Preview 与 Production:为什么域名没变?

这是最容易踩的坑:wrangler pages deploy 默认把本次上传关联到「当前 git 分支」对应的 Preview 部署,不会自动替换 Production。

现象原因
终端显示 Preview URL当前分支 ≠ Pages 控制台里的 Production branch
blog.example.com 仍是旧内容自定义域挂在 Production 部署上,Preview 只有 *.pages.dev
分支名出现在预览域例如 feat-xxx.my-blog.pages.dev

Production 部署需要显式指定与控制台一致的生产分支,例如 main

bash
npx wrangler pages deploy docs/.vitepress/dist \
  --project-name=my-blog \
  --branch=main

或在 Cloudflare Dashboard → Workers & Pages → 项目 → Deployments 里,将某次 Preview Promote to production

请在 Pages 项目 Settings → Builds & deployments 确认 Production branch(常见为 mainmaster),与 --branch 保持一致。

封装部署脚本

为避免每次手写参数,可在仓库根目录增加 deploy-cloudflare.sh,用环境变量区分预览与生产:

bash
#!/usr/bin/env sh
set -e

PROJECT="${CLOUDFLARE_PAGES_PROJECT:-my-blog}"
ENV="${CLOUDFLARE_ENV:-preview}"
PRODUCTION_BRANCH="${CLOUDFLARE_PAGES_PRODUCTION_BRANCH:-main}"
DIST_DIR="docs/.vitepress/dist"

case "$ENV" in
  production | prod)
    BRANCH="${CLOUDFLARE_PAGES_BRANCH:-$PRODUCTION_BRANCH}"
    ;;
  preview | pre)
    if [ -n "${CLOUDFLARE_PAGES_BRANCH:-}" ]; then
      BRANCH="$CLOUDFLARE_PAGES_BRANCH"
    elif [ -n "${CLOUDFLARE_PAGES_PREVIEW_BRANCH:-}" ]; then
      BRANCH="$CLOUDFLARE_PAGES_PREVIEW_BRANCH"
    else
      BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo preview)"
    fi
    ;;
  *)
    echo "error: CLOUDFLARE_ENV must be preview or production" >&2
    exit 1
    ;;
esac

pnpm run build

exec wrangler pages deploy "$DIST_DIR" \
  --project-name="$PROJECT" \
  --branch="$BRANCH"

package.json 可挂快捷命令:

json
{
  "scripts": {
    "deploy:cloudflare:preview": "CLOUDFLARE_ENV=preview bash deploy-cloudflare.sh",
    "deploy:cloudflare:production": "CLOUDFLARE_ENV=production bash deploy-cloudflare.sh"
  }
}

典型用法:

bash
# 当前功能分支 → Preview
export CLOUDFLARE_API_TOKEN="..."
pnpm run deploy:cloudflare:preview

# 上线生产域
pnpm run deploy:cloudflare:production
# 等价于 --branch=main(或你设置的 PRODUCTION_BRANCH)

自定义域名

  1. Pages 项目 → Custom domains → 添加 blog.example.com / example.com
  2. 按提示在 DNS 添加 CNAMEmy-blog.pages.dev(或 Cloudflare 托管域名时一键配置)。
  3. 确保 Production 部署已更新;Preview 不会自动绑生产域。

若同时保留 GitHub Pages,注意同一域名只能指向一处,避免 CNAME 冲突。

与 GitHub Pages 脚本并存

很多站点会保留原有 deploy.sh(构建 + push 到 username.github.io),Cloudflare 作为另一条发布通道:

通道适用场景
GitHub Pages习惯 git 驱动、开源静态仓
Cloudflare Pages要 CDN、Preview 分支、不暴露 Pages 仓

两者构建命令相同,都是 pnpm run build;仅上传目标不同。评论、统计等前端配置(如 Giscus)与托管商无关,见 VitePress 集成 Giscus 评论实践

构建失败要先修再部署

部署前务必保证 pnpm run build 通过。常见阻塞项:

  • 资源 404:Markdown 引用的图片路径大小写或后缀与 docs/public 不一致。
  • 本地搜索 duplicate ID:重复 permalink 或同名冲突文件(如同步盘产生的 *DownloadConflict* 副本)导致 MiniSearch 报 duplicate id。

构建失败时 wrangler 不会上传;即使上传了错误产物,Preview 上也会直接 404。

安全与运维建议

  • Token 最小权限:仅 Pages Edit + 必要 Account 范围;勿用 Global API Key。
  • 勿提交 Token:只通过环境变量或 CI Secret 注入;若误贴到日志,立即在 Dashboard Roll 轮换。
  • 生产发布:合并到生产分支后再跑 deploy:cloudflare:production,或在 Dashboard Promote。
  • 构建与上传分离:CI 可先 pnpm run build 缓存 dist,再单独 job 执行 wrangler pages deploy,缩短反馈时间。

小结

  1. VitePress 产物目录固定为 docs/.vitepress/dist,与 wrangler.tomlpages_build_output_dir 对齐。
  2. 非交互环境配置 CLOUDFLARE_API_TOKEN,用 wrangler pages deploy <dir> --project-name=... 直传。
  3. 不带 --branch 或分支名不等于 Production branch 时,只会更新 Preview;生产域需 --branch=main 或 Dashboard 提升。
  4. CLOUDFLARE_ENV=preview|production 封装脚本,避免功能分支误触生产。

本文为个人静态站实践脱敏整理,文中域名、项目名、路径均为示例,请按自己的环境替换。