deploy blog to github cloudflare vercel
六月 13, 2026 更新: 八月 21, 2026 [网络与安全] #deployment #github #cloudflare准备工作
先构建站点,确认 public/ 目录正常生成:
zola build
ls public/
GitHub Pages
创建仓库
创建 github.com/<你的用户名>/<你的用户名>.github.io 仓库,这是 GitHub 的 user site,域名固定为 https://<用户名>.github.io。
git init
git remote add origin git@github.com:<用户名>/<用户名>.github.io.git
GitHub Actions 自动部署
创建 .github/workflows/deploy.yml:
name: Deploy Zola Blog
on:
push:
branches: [master]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Zola
run: |
ZOLA_VERSION=$(curl -s https://api.github.com/repos/getzola/zola/releases/latest | grep tag_name | cut -d '"' -f4)
curl -sL "https://github.com/getzola/zola/releases/download/${ZOLA_VERSION}/zola-${ZOLA_VERSION}-x86_64-unknown-linux-gnu.tar.gz" | tar xz
sudo mv zola /usr/local/bin/
- name: Build
run: zola build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
推送后 GitHub Actions 自动构建并部署到 gh-pages 分支。在仓库 Settings → Pages 中选择 gh-pages 分支作为部署源。
坑:shalzz action 必须 pin 版本 tag,不能跟 master
uses: shalzz/zola-deploy-action@master 的 Dockerfile 硬编码了 zola 版本(2026-08 时为 0.23.1),ZOLA_VERSION env 变量在新版完全被忽略(传了也不生效)。当 action 更新内置 zola 后,旧主题(abridge)模板语法不兼容新版直接构建失败:
ERROR error: Found `:` but expected }}.
--> abridge/templates/categories/single.html:17:17
修复:pin 到与本地一致的版本 tag(action 的 tag 按 zola 版本命名):
uses: shalzz/zola-deploy-action@v0.19.2
排查技巧:gh run view <run-id> --log-failed 看日志里的 Using zola x.y.z 行,与本地 zola --version 对比即可发现版本漂移。
Cloudflare Pages
Cloudflare Pages 直接托管静态站点,支持自定义域名 juzhong.xyz 或子域名如 blog.juzhong.xyz。
连接 GitHub 仓库
在 Cloudflare Dashboard → Workers & Pages → Pages → 创建项目,连接 GitHub 仓库。
构建配置:
| 配置项 | 值 |
|---|---|
| 构建命令 | zola build |
| 输出目录 | public |
| 环境变量 | ZOLA_VERSION=0.19.2 |
Cloudflare Pages 内置了 Zola 支持,会自动下载对应版本。
自定义域名
Pages 项目 → 自定义域 → 添加 blog.juzhong.xyz。
需要在 juzhong.xyz 的 DNS 中添加 CNAME 记录:
blog.juzhong.xyz CNAME <项目名>.pages.dev
Cloudflare 代理(橙色云朵)开启后自动提供 HTTPS。
阿里云 VPS + Cloudflare Tunnel
适用场景:域名未备案(国内服务器直连 80/443 会被要求备案)、想自己掌控服务器。Cloudflare Tunnel 让 VPS 主动连接 Cloudflare 边缘,用户访问走 Cloudflare 的 HTTPS,源站端口无需对外开放,未备案域名也能正常访问。
架构
用户 --HTTPS--> Cloudflare 边缘 --Tunnel--> VPS localhost:2083 --Caddy file_server--> 静态文件
1. 安装 cloudflared
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
sudo install -m 755 cloudflared /usr/local/bin/cloudflared
2. 登录并创建 Tunnel
cloudflared tunnel login # 浏览器授权
cloudflared tunnel create blog-tunnel
3. 配置 Tunnel
编辑 /etc/cloudflared/config.yml:
tunnel: <tunnel-id>
credentials-file: /home/<user>/.cloudflared/<tunnel-id>.json
ingress:
- hostname: blog.juzhong.xyz
service: http://localhost:2083
- service: http_status:404
4. 绑定 DNS
cloudflared tunnel route dns <tunnel-id> blog.juzhong.xyz
等价于手动在 Cloudflare DNS 加 CNAME:blog.juzhong.xyz → <tunnel-id>.cfargotunnel.com(保持橙色云朵代理)。
5. 构建站点(关键:-u 必须匹配实际域名)
zola build -u https://blog.juzhong.xyz
为什么
-u这么重要? Zola 的搜索索引search_index.*.js里存的是绝对 URL,按base_url(即--base-url/-u参数)生成。如果构建时的域名和用户实际访问的域名不一致(比如用https://ali.juzhong.xyz构建、用户却从blog.juzhong.xyz访问),搜索能搜出结果,但点击结果会跳到错误域名 → 404。每个部署目标都要用对应的域名构建:本站用-u https://blog.juzhong.xyz,Vercel 用--base-url https://vercel.juzhong.xyz(见下文)。换域名部署时必须同步重新构建。
6. 上传到 VPS
rsync -av --delete public/ alivps:/home/peter/project/caddy-dns/blog/
7. Caddy 静态部署
服务器上 docker-compose.yaml(caddy 镜像需含 Cloudflare DNS 插件,如 slothcroissant/caddy-cloudflaredns):
services:
caddy:
image: slothcroissant/caddy-cloudflaredns:2.11.4
restart: unless-stopped
ports:
- "2083:2083"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./blog:/var/www/blog:ro
Caddyfile:
http://blog.juzhong.xyz:2083 {
root * /var/www/blog
file_server
encode gzip
}
docker compose up -d
如果 VPS 上还要托管别的站点(如 wg-easy 管理界面),Caddy 里加一个
reverse_proxy站点块共用 2083 入口即可。
Vercel
Vercel 也支持 Zola 静态站点,但默认构建环境没有 Zola 版本太低,需要自定义构建命令。
连接仓库与构建配置
访问 vercel.com/new 导入 GitHub 仓库。
Vercel 可能无法自动识别 Zola 项目,手动配置构建参数:
| 配置项 | 值 |
|---|---|
| Framework | Other |
| Build Command | 见下方自定义命令 |
| Output Directory | public |
自定义构建命令(下载指定版本 Zola 并构建):
curl -sL https://github.com/getzola/zola/releases/download/v0.19.2/zola-v0.19.2-x86_64-unknown-linux-gnu.tar.gz | tar -xzf - && ./zola build --base-url https://vercel.juzhong.xyz
拆解说明:
curl -sL ... | tar -xzf -—— 下载 Zola v0.19.2 二进制包并解压到当前目录./zola build --base-url https://vercel.juzhong.xyz—— 用本地 Zola 构建,--base-url指定站点根 URL,确保绝对路径(如 RSS、sitemap)正确
注意:将
vercel.juzhong.xyz替换为你的实际域名;如果没有自定义域名,使用 Vercel 分配的xxx.vercel.app域名。
自定义域名配置(以 Cloudflare 为例)
下面以子域名 vercel.juzhong.xyz 为例,DNS 解析由 Cloudflare 管理。
第一阶段:Vercel 平台操作
- 进入项目仪表板,点击顶部 Settings → 左侧菜单 Domains
- 输入完整域名(例如
vercel.juzhong.xyz),点击 Add - 页面会提示 Invalid Configuration(正常),点击域名旁边的 View 查看需要的 DNS 记录
Vercel 通常建议添加以下记录:
- A 记录:将根域名指向
76.76.21.21(Vercel 的 IP) - CNAME 记录:将子域名指向
cname.vercel-dns.com - cname.vercel-dns.com — 全球通用
- cname-china.vercel-dns.com — 国内专用节点,中国大陆访问速度更快、更稳定
- 由于你在国内使用,强烈推荐使用 cname-china.vercel-dns.com。
本项目使用子域名,只需 CNAME 记录即可。
第二阶段:Cloudflare DNS 操作
- 登录 Cloudflare 仪表板 → 选择域名
juzhong.xyz→ DNS → 记录 - 点击 添加记录,配置如下:
| 字段 | 值 |
|---|---|
| 类型 | CNAME |
| 名称 | vercel |
| 目标 | cname.vercel-dns.com |
| 代理状态 | 仅 DNS(灰色云朵 ☁️) |
关键:代理状态务必选择灰色云朵(仅 DNS),不要开启橙色云朵(代理)。
为什么? Vercel 使用 Let's Encrypt 自动签发 SSL 证书,需要通过 ACME 协议验证域名所有权。Cloudflare 橙色代理会拦截 ACME 验证请求,导致证书签发失败、域名始终显示 Invalid Configuration。
2026-08 更正(针对自建 Caddy):上面的说法对 Vercel 平台成立,但不能泛化——"CF 橙云拦截 ACME 验证"只拦截
tls-alpn-01挑战(CF 边缘终结 TLS,LE 协商不了 acme-tls/1),而http-01挑战走 80 端口普通转发,不受代理影响。自建 Caddy 在橙云下会 tls-alpn 失败后自动跨 order 回退 http-01 并签发成功(实测 1~2 分钟完成,全程自动)。唯一死锁场景:CF 开启 "Always Use HTTPS" + SSL 模式 Full (strict) 且源站还没有有效证书。详见 Caddy 在 CF 橙云下的 ACME 挑战实测。
特殊情况:根域名配置
DNS 标准不允许根域名(@)使用 CNAME 记录。Cloudflare 通过 CNAME Flattening 技术自动将根域名的 CNAME 转换为 A 记录,你只需正常添加 CNAME 记录,名称写 @ 即可。
如果 Vercel 提示根域名需要 A 记录,则改为:
| 字段 | 值 |
|---|---|
| 类型 | A |
| 名称 | @ |
| IPv4 地址 | 76.76.21.21 |
第三阶段:验证与生效
- DNS 记录全球生效通常需要几分钟到几十分钟
- 回到 Vercel 项目 Domains 页面,状态从红色 Invalid Configuration 变为绿色 Valid Configuration 即表示成功
- SSL 证书自动签发,通常 1-3 分钟内完成
- 访问
https://vercel.juzhong.xyz确认站点正常
额外配置:URL 重定向(可选)
在 Vercel 的 Domains 页面,点击域名旁边的 Edit,可设置:
- 将
www.vercel.juzhong.xyz重定向到vercel.juzhong.xyz(或反之) - 如需同时使用 www 子域名,需在 Cloudflare 再添加一条名称
www.vercel的 CNAME 记录,并同样加入 Vercel 项目
常见问题排查
| 问题 | 原因 | 解决 |
|---|---|---|
| 状态一直 Invalid | Cloudflare 代理未关闭(橙色云朵) | 将 DNS 记录的代理状态改为灰色(仅 DNS) |
| DNS 记录冲突 | Cloudflare 中存在旧 A/AAAA 记录与 CNAME 冲突 | 删除无关的解析记录,只保留 Vercel 所需的 |
| www 子域名无法访问 | Vercel 不会自动添加 www 子域名 | 手动在 Vercel 中添加 www.vercel.juzhong.xyz,并添加对应的 Cloudflare DNS 记录 |
| SSL 证书续期失败 | 后续开启了 Cloudflare 代理(橙色云朵),拦截了 ACME 验证 | 在 Cloudflare Zero Trust 中为路径 /.well-known/acme-challenge/* 设置 Bypass 策略;或在证书续期期间临时关闭代理(自建 Caddy 场景无需此操作,会自动回退 http-01,见上方更正说明) |
四平台对比
| GitHub Pages | Cloudflare Pages | Vercel | 阿里云 VPS + Tunnel | |
|---|---|---|---|---|
| 免费额度 | 无限 | 无限 | 100GB/月带宽 | 取决于 VPS |
| 自定义域名 | 支持 | 支持 | 支持 | 支持 |
| HTTPS | 自动 | 自动 | 自动 | 自动(Cloudflare) |
| 构建时间 | 10min/次 | 500次/月 | 6000min/月 | 无限制 |
| 域名 | xxx.github.io | xxx.pages.dev | xxx.vercel.app | 自备域名 |
| 适用场景 | 简单托管 | 自定义域名 + CDN | 开发预览 | 未备案域名 / 自己掌控服务器 |
推荐方案:源码放 GitHub,用 Cloudflare Pages 部署到 blog.juzhong.xyz。GitHub 负责版本管理,Cloudflare 提供全球 CDN 和免费 HTTPS。域名未备案或想用国内 VPS 时,改用「阿里云 VPS + Cloudflare Tunnel」方案。
无论哪种方案,部署/构建时 --base-url(或 serve -u)必须填实际访问域名,否则站内搜索不可用。