1. 背景

博客原先托管在 GitHub Pages 上,但从中国大陆访问时经常加载失败。排查后发现两个主要原因:

  1. GitHub Pages 的 IP 在国内不稳定,DNS 污染和连接超时频繁发生
  2. 外部 CDN 资源(如 cdn.jsdelivr.net)在国内访问不稳定

本文记录了从问题排查、迁移到 Cloudflare Pages、实现双域名共存,以及替换访问量统计服务的完整过程。


2. 排查博客中的外部依赖

2.1 全面扫描模板文件

layouts/ 目录下所有 .html 文件进行正则搜索,查找所有外部 URL 引用:

grep -rn "https://" layouts/ --include="*.html"

2.2 外部依赖清单

资源来源位置是否影响渲染
KaTeX(CSS/JS)cdn.jsdelivr.netextend_head.html
Mermaid 图表库cdn.jsdelivr.netbaseof.html
Twikoo 评论cdn.staticfile.orgextend_footer.html否,未启用
不蒜子访问量本地 /js/busuanzi.pure.mini.jsextend_head.html否,本地资源
Font Awesome本地 /css/font-awesome.min.cssextend_head.html否,本地资源
jQuery本地 /js/jquery-3.7.1.min.jsextend_head.html否,本地资源

2.3 结论

本地资源虽然不受 CDN 影响,但它们都托管在 GitHub Pages 上。GitHub Pages 本身打不开,这些资源自然也无法加载。因此,替换个别 CDN 源无法从根本上解决问题,需要更换整个托管平台。


3. 方案选择与迁移

3.1 方案对比

方案成本效果难度
绑定自定义域名 + Cloudflare CDN 代理域名费简单
Cloudflare Pages 替代 GitHub Pages免费最好中等
部署到 Vercel免费中等
同步部署到 Gitee Pages免费一般(需备案)复杂

最终选择了 Cloudflare Pages,自带全球 CDN(含亚太节点),国内访问速度有明显改善。

3.2 创建 Cloudflare Pages 项目

  1. 注册 Cloudflare 账户:https://dash.cloudflare.com/sign-up
  2. 左侧菜单 → Workers & PagesCreatePagesConnect to Git
  3. 授权 GitHub,选择源码仓库
  4. 配置构建参数:
设置项
Production branchmain
Framework presetHugo
Build commandgit submodule update --init --recursive && hugo --gc --minify
Build output directorypublic
环境变量 HUGO_VERSION0.147.9
环境变量 NODE_VERSION18
  1. 点击 Save and Deploy,等待 2-5 分钟
  2. 构建成功后得到免费域名:<项目名>.pages.dev

注意:Build command 中必须加 git submodule update --init --recursive,因为 PaperMod 主题是通过 git submodule 引用的。缺少这一步,Cloudflare Pages 构建时会找不到主题文件而报错。


4. 实现双域名共存

迁移完成后,希望 GitHub Pages 和 Cloudflare Pages 都能正常访问。但遇到了一个问题:pages.dev 上点击站内链接,页面会跳转到 github.io

4.1 问题原因

Hugo 模板中大量使用了以下函数,它们会根据 baseURL 拼接完整域名:

函数生成结果
.Permalinkhttps://xxx.github.io/posts/xxx/
| absURLhttps://xxx.github.io/img/xxx.jpg
| absLangURLhttps://xxx.github.io/zh/xxx/

所有链接都指向 github.io,从 pages.dev 访问时点击链接必然跳转。

4.2 尝试 relativeURLs: true

最初尝试在 config.yml 中设置:

baseURL: https://xxx.github.io/
relativeURLs: true
canonifyURLs: false

relativeURLs: true 的设计意图是让 Hugo 生成相对路径链接。但在 Hugo 0.147.9 中,这个配置只对部分内容生效,模板中使用 | absURL.Permalink| absLangURL 生成的链接不受其影响,仍然输出绝对 URL。即使在 localhost:1313 本地开发时,点击页面链接也会跳转到 github.io

4.3 最终方案:baseURL: /

baseURL 设置为根路径 /

baseURL: /
relativeURLs: true
canonifyURLs: false

这样 Hugo 生成的所有链接都变成 /posts/xxx//img/xxx.jpg 这样的根路径形式。浏览器会自动在当前域名下解析这些路径,无论从 github.iopages.dev 还是 localhost 访问,链接都不会跨域跳转。

4.4 数据流向

迁移后的架构如下:

flowchart TD
    A["源码仓库"] -->|"git push"| B["GitHub"]
    B -->|"触发 GitHub Action"| C["GitHub Action 构建"]
    B -->|"Cloudflare 检测到变更"| D["Cloudflare Pages 构建"]
    C -->|"推送静态文件"| E["xxx.github.io"]
    D -->|"部署到全球 CDN"| F["xxx.pages.dev"]
    E -->|"国内访问慢"| G["中国大陆用户"]
    F -->|"亚太节点加速"| G

两条构建通道完全独立,都从同一个源码仓库触发,生成的内容完全一致。


5. baseURL: / 的影响分析

5.1 正常工作的部分

方面状态说明
github.io 访问所有链接都是根路径
pages.dev 访问所有链接都是根路径
站内页面跳转不会跨域跳转
图片加载当前域名加载
搜索功能正常工作
暗色/亮色主题不涉及外部 URL
数学公式(KaTeX)从 jsdelivr CDN 加载,与域名无关

5.2 有轻微影响的部分

方面影响严重程度
RSS 订阅源<link><guid> 变成相对路径,大部分阅读器兼容轻微
社交分享图片og:image 等变成相对路径,社交平台爬虫可能无法加载预览图轻微
sitemap.xml链接变成相对路径,搜索引擎能正常处理无影响

对于个人博客来说,这些影响完全可以接受。


6. 替换访问量统计服务:不蒜子 → Vercount

迁移后需要确认双域名下的访问量统计。原先使用的是不蒜子(busuanzi),但存在以下问题:

  1. 不蒜子以访问域名为 key 分别计数,双域名下数据不互通
  2. 不蒜子使用 Referrer 方法统计,在部分浏览器和移动端上不准确
  3. 不蒜子的后端服务稳定性一般

6.1 选择 Vercount

Vercount 是一个基于 Go + Redis 的开源网站流量计数器,由 Next.js 提供后台。相比不蒜子,它有以下优势:

特性不蒜子Vercount
统计方法Referrer(过时)POST 请求(准确)
移动端兼容一般
数据同步自动同步不蒜子历史数据
后端架构较旧Go + Redis
兼容性-兼容不蒜子的 span 标签

6.2 集成方法

第一步:添加脚本

layouts/partials/extend_head.html 中,将不蒜子的本地脚本:

<script src="/js/busuanzi.pure.mini.js"></script>

替换为 Vercount 的远程脚本:

<script defer src="https://events.vercount.one/js"></script>

第二步:替换 span ID

layouts/partials/footer.html 中,将不蒜子的 span 标签:

<span id="busuanzi_container">
    <span class="fa fa-user"></span> <span id="busuanzi_value_site_uv"></span>
    <span class="fa fa-eye"></span> <span id="busuanzi_value_site_pv"></span>
</span>

替换为 Vercount 的 span 标签:

<span id="vercount_container">
    <span class="fa fa-user"></span> <span id="vercount_value_site_uv">Loading</span>
    <span class="fa fa-eye"></span> <span id="vercount_value_site_pv">Loading</span>
</span>

第三步:删除不蒜子本地文件

确认 Vercount 正常工作后,删除 static/js/busuanzi.pure.mini.js

注意:Vercount 会自动同步不蒜子的历史数据,首次访问时即可看到旧数据被同步过来。

附:本博客坚持采用Canary Deployment进行发布更新与Bug修复,之前的操作是本地实践Canary,如今可以尝试使用此种双域名进行一些很炫酷但不知道稳不稳定的小功能的前端 JS 动态加载,进行真·Canary分流。可供参考:金丝雀发布的使用方法