Featured image of post 从购买域名到自动发布:我的 Cloudflare 技术博客搭建记录

从购买域名到自动发布:我的 Cloudflare 技术博客搭建记录

从 NameSilo 注册域名、接入 Cloudflare DNS,到 Hugo Stack、私有 GitHub 仓库与 Decap 后台,再到让 agent 通过 Git 发布文章,记录完整流程和踩过的坑。

— 次阅读

我想搭一个用来记录技术学习和成长的博客:有自己的域名,首页能展示图片,文章按时间和标签整理;平时可以在网页里写,也可以把想法交给 agent,让它整理成 Markdown 后发布。

刚开始,我并没有完全分清域名、DNS、服务器和托管平台的关系。买到 zouyiyi.top 后,我先后问过:域名到底怎么用?Cloudflare 会分配一台服务器吗?没有公网 IP 怎么放博客?blog.zouyiyi.top 和 api.zouyiyi.top 是不是还要再买?

这篇文章记录这次实际搭建的路径,以及后来“后台已经发布,前台却没更新”“点开文章是 404”的排查过程。最终使用的是 Cloudflare Workers Static Assets 托管博客,Workers Builds 自动构建;没有租一台需要自己维护的 VPS。

先看最终结构:每个服务负责什么

组件本次负责的事情
NameSilo域名注册和续费管理
Cloudflare DNS管理 zouyiyi.top 的 DNS 解析
Hugo + Stack把 Markdown 和图片生成静态网站
GitHub 私有仓库保存文章、图片、主题配置和版本历史
Decap CMS提供浏览器里的文章编辑界面
独立的登录 Worker完成 GitHub 登录回调,检查站点所有者身份
Cloudflare Workers Builds监听生产分支,自动构建并部署
Cloudflare Workers Static Assets向读者提供生成后的 HTML、CSS、JavaScript 和图片

域名解析、两种写作入口和 Cloudflare 自动发布的关系

这里有两条不同的链路:读者通过域名访问已经生成的网站;作者通过 Git 修改源文件,再让构建系统更新网站。写作时使用的编辑器可以变化,最终都汇入同一个仓库。

1. 买域名:注册商与网站托管可以分开

我先比较了包含 zouyiyi 等名称的域名,也考虑过 .top、.xyz 这样的后缀。过程中尝试了 Porkbun,但遇到身份验证没有通过的问题,最终在 NameSilo 注册了 zouyiyi.top。

这只是本次注册的经历。我没有拿到身份验证失败的明确原因,不能据此判断某个平台一定不支持某个国家的用户,也不建议靠随意填写身份资料来解决验证问题。价格方面,这里不记录容易过期的促销数字;购买时应分别看首年费用和后续续费费用。

买完域名后,我最先弄明白的是:域名注册商、DNS 服务商和网站托管服务商可以是不同的公司。

在这套方案里,域名仍然在 NameSilo 管理和续费,DNS 交给 Cloudflare,博客文件也部署到 Cloudflare。把 DNS 接入 Cloudflare,并不等于把域名注册转移到了 Cloudflare Registrar。

2. 接入 Cloudflare:修改的是 Nameservers

我先把根域名 zouyiyi.top 添加到 Cloudflare。Cloudflare 为这次接入分配了两条权威名称服务器地址:

1
2
chin.ns.cloudflare.com
denver.ns.cloudflare.com

然后在 NameSilo 的 Domain Manager 中选中域名,进入 Change Nameservers,将原来的名称服务器替换为 Cloudflare 分配的地址,保存后等待接入状态变为 Active。

上面两条地址属于本次域名的分配结果。给自己的域名接入时,应复制自己 Cloudflare 页面里的地址,不要照抄。Cloudflare 的完整接入流程也要求在注册商处更新 nameservers;如果域名已有 DNS 记录或 DNSSEC,还需要按官方迁移步骤处理。Cloudflare:完整 DNS 接入流程

可以用下面的命令辅助检查:

1
2
dig NS zouyiyi.top @1.1.1.1
dig NS zouyiyi.top @8.8.8.8

需要区分的是:Nameservers 决定“由谁回答这个域名的 DNS 查询”;A、AAAA、CNAME 等记录决定具体名称指向什么。接入 Cloudflare 时,不能把它给出的 nameserver 地址当作博客服务器的 IPv4 地址填写。

3. 有了域名,为什么不需要自己的公网 IP

我最后把博客放在 blog.zouyiyi.top,给登录回调使用 blog-auth.zouyiyi.top。它们都是 zouyiyi.top 下面的子域名,不需要再购买一个独立域名,但仍然需要配置相应的解析或服务绑定。

这次博客使用 Cloudflare 的托管基础设施。Hugo 生成的文件上传到 Workers Static Assets,读者访问时由 Cloudflare 提供这些资源,因此我不需要准备一台有固定公网 IP 的个人服务器。Cloudflare Workers Static Assets

具体做法是在博客 Worker 上绑定自定义域名 blog.zouyiyi.top,随后检查 DNS、HTTPS 和实际页面访问,而不是只看到控制台出现一条配置就认为完成。

这也没有把整个域名锁定在一种托管方式里。例如,以后可以让博客继续使用 Cloudflare 托管,同时为 api.zouyiyi.top 配置指向自己服务器的记录。不同子域名可以服务于不同用途。给某个子域名添加 A 记录,并不意味着其他子域名必须迁移到那台服务器。

4. 选择 Hugo Stack:文章仍然是普通文件

最初上线的主题是 PaperMod,后来为了让首页更有视觉层次,换成了 Hugo Stack。我希望它主要是一个学习博客,而不是按章节组织的产品文档站:以时间线展示文章,配合分类、标签、搜索、目录和代码高亮。

本次固定使用 Hugo Extended 0.169.0 和 Stack 4.0.3。固定版本是为了让本机和自动构建使用一致的工具;以后升级应单独验证,不把这些版本号当作永远不变的最新版本。

仓库里的文章统一使用 Hugo 的页面包结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
content/
  post/
    domain-to-cloudflare-blog/
      index.md
      cover.svg
      architecture.svg
  page/
    about/index.md
    archives/index.md
    search/index.md

一篇文章就是一个目录。index.md 放正文和文章信息,图片可以放在旁边。首页的封面、文章摘要和分类来自 Markdown 开头的 YAML 信息。

主题只负责展示。文章没有被锁进某个编辑器的私有格式,以后可以直接用文本编辑器、Git 工具或 agent 修改。

5. Decap 是网页编辑器,登录权限是另一回事

我希望有一个能直接写文章、上传图片的后台,但又担心:放在公网,别人是不是也能编辑?

本次的后台位于 /admin/。Decap 的前端文件和博客一起部署到 Cloudflare,在浏览器里运行;它不是一台必须在我电脑上保持运行的后台服务器。它通过 GitHub 后端读写仓库,保存文章时实际是在修改文件。

我为它单独配置了 GitHub OAuth 登录应用,并用另一个 Worker 处理授权回调。应用里的两个地址分别是:

1
2
3
4
5
Homepage URL:
https://blog.zouyiyi.top

Authorization callback URL:
https://blog-auth.zouyiyi.top/callback

登录 Worker 不只检查“是否登录了 GitHub”,还核对预先允许的 GitHub 数字账号 ID,并检查该账号是否具有博客仓库的写权限。仓库本身是私有的,没有把其他用户加入写作协作。

因此,访客能打开后台登录页,并不代表能读取仓库或修改文章。这里保护的是对内容的读写权限,不是靠隐藏 /admin/ 地址来保护网站。

配置时还遇到一个细节:我一开始把 GITHUB_CLIENT_SECRET 添加成了普通 Variable,后来改成了 Secret。登录服务使用的密钥只保存在 Cloudflare 的服务端绑定中,不应写进前端 JavaScript、仓库或文章示例。OAuth 需要的服务端流程可以参考 Decap 的 GitHub 后端说明。

6. 点“发布”后,代码怎样到达 Cloudflare

为了区分草稿和正式文章,我启用了 Decap 的编辑工作流:

1
2
3
4
5
6
7
backend:
  name: github
  repo: YOUR_ACCOUNT/YOUR_PRIVATE_REPOSITORY
  branch: main
  squash_merges: true

publish_mode: editorial_workflow

保存草稿时,Decap 会创建草稿分支和拉取请求;后续保存继续更新草稿,正式发布则合并到生产分支。这是编辑按钮与 Git 操作之间的对应关系。Decap:Editorial Workflows

接着,在现有博客 Worker 的 Settings → Builds → Connect 中连接 GitHub,授权访问博客私有仓库。本次配置为:

设置值
生产分支main
根目录/
构建命令python3 scripts/build.py
部署命令python3 scripts/deploy.py
监听路径**
非生产分支和预览部署关闭

这里的两个 Python 文件是本项目自己的脚本,不是 Cloudflare 内置命令。build.py 下载并校验固定版本的 Hugo Extended,再生成 dist/;deploy.py 收集静态资源,通过 Cloudflare API 上传并部署。构建凭证由 Cloudflare 的构建环境提供,不写进 Git。

Cloudflare 支持配置构建命令、部署命令、生产分支和构建凭证。复用这套思路时,需要让命令与自己的项目工具匹配,不能只复制两行命令却没有相应脚本。Workers Builds 配置

这次选择了 Cloudflare 自带的 Workers Builds,没有再配置一套 GitHub Actions。两者都是可行的自动化入口,没必要为了“自动发布”同时维护两套。

实际链路是:

1
2
3
4
5
6
7
8
9
Decap 点击发布,或者本地 git push
                  ↓
         GitHub main 出现新提交
                  ↓
       Cloudflare 拉取仓库并运行 Hugo
                  ↓
          上传 dist 中的静态资源
                  ↓
        blog.zouyiyi.top 展示新内容

关闭非生产分支部署,是为了让草稿继续留在私有仓库里,不因为一个预览网址而公开。

7. 踩坑:发布成功不等于网站已经更新

后台登录成功、能看到文章之后,我创建了一篇测试文章。随后发现两个问题:新文章没出现在首页,后台点“查看文章”也会遇到 404。

文章已经进了 GitHub,但没有新的构建

排查发现,测试文章对应的拉取请求已经合并到 main,Markdown 和图片也都在仓库里。但 Cloudflare 的最新成功构建仍对应更早的一次提交。

这就把问题缩小到了自动构建触发这一段,而不是“Decap 没保存文章”或者“Hugo 一定无法识别 Markdown”。

当时的监听规则是 *。调整为 ** 后,我又做了一次只修改文章子目录文件的合并测试,确认 Cloudflare 自动启动构建,并在公网看到了修改。

这里需要保留一个边界:我没有完整的 webhook 投递记录,不能仅凭这一组现象证明 * 是唯一根因,更不能把“单个星号只匹配根目录”当作 Cloudflare 的通用规则。官方文档对通配符有自己的定义。此次能确认的是漏构建的现象,以及调整后的文章目录合并测试已经通过。Cloudflare:Build watch paths

真正应该留下来的检查习惯是:自动发布要用文章文件验证,不能只改根目录 README,看到构建成功就结束。

后台预览地址把 index 拼进了网址

另一个问题来自两套网址生成规则没有统一。

文章文件放在 content/post/<文章目录>/index.md 中。最初 Decap 配置使用:

1
preview_path: 'posts/{{slug}}/'

但在这个位置,Decap 的 {{slug}} 表示整条条目标识,并不总是想象中的短文章名。它包含的 index 部分可能生成类似 /posts/hello-world-index/ 的地址,而 Hugo 的实际页面地址没有这个后缀。两者不一致,自然就是 404。Decap:preview_path 的变量含义

最后统一采用文章目录名决定网址:

1
2
3
4
5
6
7
# hugo.yaml
permalinks:
  page:
    post: /posts/:contentbasename/
    page: /:slug/
  section:
    post: /posts/
1
2
3
4
# static/admin/config.yml 中的文章集合
folder: content/post
path: '{{slug}}/index'
preview_path: 'posts/{{dirname}}/'

这样,content/post/domain-to-cloudflare-blog/index.md 对应的就是:

1
https://blog.zouyiyi.top/posts/domain-to-cloudflare-blog/

这里也补上了 section.post 的配置。只配置单篇文章的路径,并不代表 /posts/ 这个列表页会自动使用同样的地址。Hugo 可以分别设置单页和栏目页的链接规则。Hugo:Permalinks

修复后实际检查了首页、文章列表、每篇文章、图片,以及后台加载到的新配置。曾经生成的错误地址也做过兼容跳转;后来清理测试文章时,对应的临时跳转一并移除了。

8. 不用网页后台,直接添加 Markdown 也能发布

配置完成后,我意识到 Decap 只是其中一种编辑入口。既然它最后写入的也是 Git 仓库,那直接拉取代码、添加文章,再推送到 main,就可以走同一条自动发布链路。

新文章可以这样组织:

1
2
3
4
content/post/my-learning-note/
├── index.md
├── cover.webp
└── diagram.png

index.md 的开头写文章信息,后面就是普通 Markdown:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
---
title: "我的一次学习记录"
date: 2026-11-13T01:00:00+08:00
draft: false
description: "这次遇到的问题、尝试过程和得到的结论。"
image: cover.webp
categories: ["学习笔记"]
tags: ["实践"]
---

## 遇到了什么问题

先描述背景,再记录尝试和验证。

![过程示意图](diagram.png)

实际写作时要替换为自己的时间、正文和图片;没有图片就省略相关字段,不能留下不存在的文件路径。draft: true 会让 Hugo 跳过文章;未来时间的文章同样不会在当前构建里出现。把时间设到未来也不等于已经安排好了定时发布,时间到了仍需要一次新的构建。

我现在的发布操作是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 在仓库根目录,先确认没有需要保护的未提交修改
git status --short
git pull --ff-only

# 写好文章后,构建并检查公开文件
python3 scripts/build.py
python3 scripts/deploy.py --check

# 只暂存本次文章与图片
git add content/post/my-learning-note/
git diff --cached
git commit -m "docs: 发布学习笔记"
git push origin main

上述推送命令适用于当前就在 main 的情况。如果在其他分支写作,需要检查后合并到 main 才会发布。deploy.py --check 只检查资源,并不会直接上线;真正的部署仍由 Cloudflare 执行。

9. 把写作规则交给 agent:仓库里的 AGENTS.md

为了让后续 agent 不必重新摸索项目,我在仓库根目录增加了 AGENTS.md,把这些约定写成可以执行的步骤:

  • 新文章放进 content/post/<目录名>/index.md,配图放在同一目录。
  • 按用户提供的想法写作,不虚构经历、实验结果或已经验证的结论。
  • 区分“整理草稿”和“发布上线”;只有明确授权发布时才推送生产分支。
  • 使用当前日期和正确时区,检查草稿状态、封面和文章链接。
  • 提交前同步仓库、检查差异,不覆盖网页后台写入的新内容。
  • 推送之后继续检查构建和真实网页,不能停在“Git 提交成功”。

以后可以直接给 agent 这样的任务:

先阅读仓库根目录的 AGENTS.md,把下面的想法整理成一篇学习笔记,检查后提交并推送到 main,确认上线后把文章链接发给我。

agent 需要的是博客仓库的正常 GitHub 写权限。日常写文章不需要拿到 Cloudflare 密钥,也不需要接管浏览器里的登录会话。至于具体工具是否自动读取这个文件,应以工具行为为准;在任务里明确要求“先读 AGENTS.md”最直观。

10. 我现在怎样判断“发布完成”

这次搭建让我不再只看某个按钮是否显示成功,而是把结果拆开检查:

检查位置要看到的结果
域名接入Cloudflare 状态为 Active,权威 NS 与分配结果一致
GitHub正文和图片已经进入 main,提交内容符合预期
Cloudflare Builds能找到对应提交的构建,结果为成功
公开博客文章网址正常打开,显示本次正文,图片可加载
首页与列表新文章能被找到,链接没有多余的 index
草稿与权限普通访客没有写入权限,草稿分支没有公开部署

目前,博客的日常维护已经收敛为一件很具体的事:把值得留下的学习过程写成文件,并验证这些文件确实变成了可访问的文章。

域名、DNS、登录和部署解决的是基础设施问题。接下来更重要的,是持续记录真实遇到的问题、尝试过的办法,以及最后弄明白了什么。