前言

博客在本地已经搭建完成,也做好了一些优化和个性化配置,万事俱备,只欠东风,胜利就在前方。接下来我们开始着手将其部署到远程托管平台。

目前公认的稳定且免费的托管方案主要有两个: GitHub PagesCloudFlare Pages,两个都行,但成年人不做选择,当然是两个都要了。

下面是一份针对部署到这两个平台的保姆级教程, 一步一步跟着做,保你通关。


二、准备工作

2.1 你需要的东西

项目 说明
GitHub 账号 免费版即可
Cloudflare 账号 免费版即可
本地 Hugo 站点 已经能正常 hugo server 预览
域名(可选) 两个平台都支持自定义域名

三、第一步:创建仓库 A(源码仓库)

3.1 在 GitHub 创建仓库

  1. 打开 github.com/new
  2. Repository nameblog-source(可以随便取,比如 my-blog
  3. DescriptionHugo blog source code
  4. VisibilityPrivate (这里选公开或私有都可以,没有强制要求)
  5. Initialize this repository with勾选 Add a README file
  6. 点击 Create repository

3.2 本地 Hugo 站点关联到仓库 A

打开终端,进入你的 Hugo 站点根目录(包含 hugo.tomlcontent/ 的文件夹):

bash
# 初始化 git(如果还没初始化)
git init

# 添加远程仓库(把 你的用户名 替换成你的 GitHub 用户名)
git remote add origin https://github.com/你的用户名/blog-source.git

# 添加所有文件
git add .

# 提交
git commit -m "Initial commit: Hugo blog source"

# 推送到 main 分支
git push -u origin main

如果提示 error: failed to push some refs,先执行 git pull origin main --rebase 再 push。


四、第二步:配置 Cloudflare Pages(自动构建)

4.1 授权 Cloudflare 访问你的私有 GitHub 仓库

  1. 登录 Cloudflare Dashboard
  2. 左侧菜单找到 Workers & Pages(或直接在首页搜索框搜 “Pages”)
  3. 点击 Create a project → 选择 Pages 标签
  4. 点击 Connect to Git
  5. 首次使用会弹出 GitHub 授权页面,点击 Authorize CloudflarePages
  6. 授权后,在仓库列表中找到你的私有仓库 blog-source,点击 Begin setup

4.2 配置构建设置

Set up builds and deployments 页面填写:

配置项 填写内容
Project name blog-source(会自动填充,可以改,只能小写字母、数字、连字符)
Production branch main
Framework preset 下拉选择 Hugo
Build command hugo --minify
Build output directory public

如果下拉框里没有 Hugo,选择 None,然后手动填写上面两项。

4.3 设置环境变量(重要)

向下滚动到 Environment variables (advanced),点击 Add variable

变量名 说明
HUGO_VERSION 0.146.7 填写你本地 hugo version 看到的版本号,确保一致
HUGO_ENV production 告诉 Hugo 这是生产环境

⚠️ 务必设置 HUGO_VERSION,否则 Cloudflare 可能用很旧的 Hugo 版本构建,导致失败。

点击 Save and Deploy

4.4 等待首次构建

Cloudflare 会自动拉取仓库 A 的代码并构建。构建完成后会显示:

  • Your site was deployed 和一个 *.pages.dev 的预览链接
  • 点击链接即可查看

4.5 (可选)绑定自定义域名

  1. 进入 Cloudflare Pages 项目 → Custom domains
  2. 点击 Set up a custom domain
  3. 输入你的域名(如 blog.example.com
  4. 按提示添加 DNS 记录(如果在 Cloudflare 托管域名,通常自动完成)

五、第三步:创建仓库 B(GitHub Pages 目标仓库)

5.1 创建公开仓库

  1. github.com/new
  2. Repository name你的用户名.github.io
    • 例如你的 GitHub 用户名是 zhangsan,就填 zhangsan.github.io
    • ⚠️ 必须严格符合这个格式,才能使用 https://zhangsan.github.io 这个默认域名
    • 如果你不想用这个域名,可以用任意名称(如 blog-public),但访问地址会变成 https://zhangsan.github.io/blog-public/
  3. VisibilityPublic ⚠️ GitHub Pages 免费版必须公开
  4. Initialize this repository with勾选 Add a README file
  5. 点击 Create repository

5.2 开启 GitHub Pages

  1. 进入仓库 B → 顶部菜单 Settings
  2. 左侧菜单找到 Pages(在 Code and automation 下面)
  3. Source 部分,选择 Deploy from a branch
  4. Branch:选择 main,文件夹选择 /(root)
  5. 点击 Save

此时页面会提示:Your site is ready to be published at https://你的用户名.github.io/


六、第四步:配置 GitHub Actions(仓库 A → 仓库 B)

这是核心步骤。我们需要在仓库 A 中创建一个工作流,每次推送时自动构建 Hugo,然后把 public/ 文件夹推送到仓库 B。

6.1 创建 Personal Access Token (PAT)

因为要从私有仓库 A 推送到仓库 B,需要一个有权限的 Token:

  1. 点击 GitHub 右上角头像 → Settings
  2. 左侧最下方 Developer settingsPersonal access tokensTokens (classic)
  3. 点击 Generate new token (classic)
  4. NoteDeploy to GitHub Pages repo
  5. Expiration:选择 No expiration(或 90 天,到期后重新生成)
  6. Select scopes:勾选以下权限:
    • repo(完整仓库控制)
    • workflow(如果需要更新 Actions)
  7. 点击页面底部 Generate token
  8. ⚠️ 立即复制这个 token(页面关闭后无法再次查看)

6.2 将 Token 存入仓库 A 的 Secrets

  1. 进入你的私有仓库 Ablog-source
  2. 顶部菜单 Settings → 左侧 Secrets and variablesActions
  3. 点击 New repository secret
  4. NameGH_PAGES_TOKEN
  5. Secret:粘贴刚才复制的 PAT
  6. 点击 Add secret

6.3 创建工作流文件

在本地 Hugo 站点根目录,创建文件夹和文件:

bash
mkdir -p .github/workflows

然后创建文件 .github/workflows/deploy.yml,内容如下:

yaml
name: Deploy to GitHub Pages

on:
  push:
    branches: [main]
  workflow_dispatch:  # 允许手动触发

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
      # 1. 检出源码
      - name: Checkout source
        uses: actions/checkout@v4
        with:
          submodules: recursive  # 如果用了 git submodule 管理主题,必须加
          fetch-depth: 0

      # 2. 安装 Hugo Extended
      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: 'latest'
          extended: true  # 如果主题用了 SCSS/Sass,必须开 extended

      # 3. 构建站点
      - name: Build
        run: hugo --minify

      # 4. 推送到仓库 B(GitHub Pages)
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v4
        with:
          personal_token: ${{ secrets.GH_PAGES_TOKEN }}
          external_repository: 你的用户名/你的用户名.github.io  # 修改这里!
          publish_dir: ./public
          publish_branch: main
          commit_message: "Deploy from blog-source: ${{ github.event.head_commit.message }}"
          cname: ""  # 如果有自定义域名,填在这里,如 blog.example.com

6.4 修改配置中的占位符

把上面 YAML 中的这两个地方改成你的:

  1. external_repository: 你的用户名/你的用户名.github.io

    • 例如:external_repository: zhangsan/zhangsan.github.io
  2. 如果用了自定义域名,把 cname: "" 改成你的域名,如 cname: blog.example.com

6.5 提交并推送

bash
git add .github/workflows/deploy.yml
git commit -m "Add GitHub Actions for dual deployment"
git push origin main

推送后,进入仓库 A → Actions 标签,应该能看到工作流正在运行。等待它变绿(✅)。

6.6 验证 GitHub Pages

  1. 进入仓库 B,确认 main 分支有了新提交(来自 Actions 机器人)
  2. 进入仓库 B → Settings → Pages
  3. 看到绿色提示:Your site is live at https://你的用户名.github.io/
  4. 点击访问

七、第五步:开关控制(停用/启用某个平台)

7.1 停用 Cloudflare Pages

方法 A(推荐):暂停自动构建

  1. 登录 Cloudflare Dashboard
  2. 进入你的 Pages 项目
  3. 顶部菜单 SettingsBuilds & deployments
  4. 找到 Pause deployments 按钮,点击暂停
  5. 需要恢复时,点击 Resume deployments

方法 B:取消 GitHub 关联

  1. Pages 项目 → SettingsGit
  2. 点击 Disconnect 断开与仓库 A 的关联
  3. 恢复时重新 Connect

7.2 停用 GitHub Pages

  1. 进入仓库 B → Settings → Pages
  2. Source 下拉框选择 None
  3. 点击 Save
  4. 站点会显示 404,表示已停用
  5. 恢复时重新选择 main 分支

7.3 更优雅的开关:用 GitHub Actions 条件控制

如果你不想登录后台,也可以在仓库 A 的 Actions 中加开关变量:

  1. 仓库 A → Settings → Secrets and variables → Actions → Variables
  2. 新建变量:
    • Name: ENABLE_GITHUB_PAGES
    • Value: true(启用)或 false(停用)
  3. 修改工作流文件,在 Deploy 步骤加 if 条件:
yaml
      - name: Deploy to GitHub Pages
        if: ${{ vars.ENABLE_GITHUB_PAGES == 'true' }}
        uses: peaceiris/actions-gh-pages@v4
        with:
          ...

这样你只需要改一个变量值就能控制是否推送到仓库 B,连代码都不用改。


八、第六步:日常写作流程

配置完成后,你每次写博客只需要做:

bash
# 1. 本地写新文章
hugo new content posts/新文章.md

# 2. 编辑内容
# 用你喜欢的编辑器打开 content/posts/新文章.md

# 3. 本地预览
hugo server -D

# 4. 满意后提交
git add .
git commit -m "Add new post: 文章标题"

# 5. 推送(一键触发双平台部署)
git push origin main

推送后:

  • Cloudflare Pages:自动检测推送,约 30 秒~2 分钟完成构建
  • GitHub Pages:Actions 自动运行,约 1~3 分钟完成构建并推送

九、注意事项与优化

9.1 构建版本一致性

Cloudflare 和 GitHub Actions 可能使用不同版本的 Hugo。建议:

  • Cloudflare:已在环境变量中设置了 HUGO_VERSION
  • GitHub Actions:在 peaceiris/actions-hugo@v3 中,hugo-version: 'latest' 会自动拉最新版。如果你需要固定版本,改成具体版本号如 hugo-version: '0.146.7'

9.2 关于 baseURL

Hugo 构建时 baseURL 只能有一个。两个平台的域名不同,解决方案:

方案 A(推荐):使用相对路径

修改 hugo.toml

toml
baseURL = '/'

这样无论部署到哪个域名,链接都能正常工作。

方案 B:分别构建两次

在 Actions 中构建两次,分别指定不同 baseURL。但这会显著增加构建时间,不推荐。

9.3 主题作为子模块

如果你用 git submodule 管理主题(如 PaperMod),确保:

  1. 本地添加子模块:

    bash
    git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
  2. .github/workflows/deploy.yml 中已包含 submodules: recursive

  3. Cloudflare Pages 构建时也会自动拉取子模块(默认行为)

9.4 构建失败排查

如果 Actions 失败,点击失败的运行记录,查看日志:

常见错误 原因 解决
hugo: command not found Actions 没装 Hugo 检查 peaceiris/actions-hugo 步骤
permission denied Token 权限不足 重新生成 PAT,勾选 repo
remote: Repository not found 仓库名写错 检查 external_repository
Cloudflare 构建失败 Hugo 版本不兼容 检查 HUGO_VERSION 环境变量

9.5 安全性提醒

  • PAT (Personal Access Token) 拥有你 GitHub 账户的仓库写入权限,不要泄露
  • 如果 Token 泄露,立即去 GitHub Settings → Developer settings → Tokens 中 Delete 并重新生成
  • 建议给 Token 设置过期时间(如 90 天),定期轮换

十、总结

你的方案完全可行,而且设计得很合理:

优点 说明
源码始终私有 仓库 A 是私有的,核心资产受保护
Cloudflare 零配置 直接 Git 集成,无需 Actions,简单可靠
GitHub Pages 可控 通过 Actions 推送,开关方便
双平台冗余 一个挂了另一个还能访问

唯一的"缺陷"是推送到仓库 A 时,Cloudflare 和 Actions 会并行构建,资源有点浪费,但功能完全正常。如果你介意,可以把 Cloudflare 也改成通过 Actions 部署(用 cloudflare/pages-action),但那样配置更复杂,你的当前方案已经是最优解。

跟着上面的步骤一步一步做,应该能顺利跑通。如果遇到具体报错,把错误日志贴出来我可以继续帮你排查。