用腾讯云 COS + EdgeOne 搭建「AI 可读」的 Markdown 博客
折腾了一天,把一个静态博客从零搭起来,部署到已备案域名、国内加速,而且每篇文章都有一个纯 Markdown 链接可以直接丢给 AI 读。这篇文章记录完整过程和踩过的坑,希望能帮人少走弯路。
目标
- 内容用 Markdown 写
- 给人看:渲染好的 HTML 页面
- 给 AI 看:复制一个链接,AI 直接读到纯 Markdown(不带 HTML 模板噪音)
- 部署到已备案域名,国内访问快
- 成本尽可能低
方案选型
对比了三种方案:
| 方案 | 回源 | 国内速度 | 备注 |
|---|---|---|---|
| GitHub Pages + EdgeOne 加速 | 有源站(GitHub 美国),未命中要跨洋回源 | 一般 | 回源配置复杂(Host/SNI 分离坑) |
| EdgeOne Pages 直发 | 无源站 | 好 | 较新产品,国内加速需确认免费版 |
| COS + EdgeOne | COS 作源站,同生态 | 好 | 成本近零,配置成熟 ← 选这个 |
最终选 COS + EdgeOne:腾讯云备案域名天然互通,COS 存储便宜,EdgeOne 回源 COS 走内部 CDN 回源流量(比公网便宜),静态内容缓存命中率高、回源极少。
整体架构
访客 ──HTTPS──▶ EdgeOne 边缘节点(国内) ──回源HTTPS──▶ COS 桶(源站)
│
├─ 命中缓存 → 直接返回(快)
└─ 未命中 → 回源 COS(一次性)
部署步骤(精简版)
用 tccli 全自动化。前置:域名腾讯云备案、EdgeOne 站点 Area=global、tccli 配好 cos/teo/dnspod。
1. 建 COS 桶(公有读)
tccli cos create_bucket --bucket blog-<APPID> --region ap-beijing --acl public-read
2. 建 EdgeOne 加速域名,回源 COS
tccli teo CreateAccelerationDomain \
--ZoneId <zone-id> --DomainName blog.example.com \
--OriginInfo '{"OriginType":"IP_DOMAIN","Origin":"<bucket>.cos.ap-beijing.myqcloud.com","HostHeader":"<bucket>.cos.ap-beijing.myqcloud.com"}' \
--OriginProtocol HTTPS --HttpsOriginPort 443
3. 加 CNAME(DNSPod)
tccli dnspod CreateRecord --Domain example.com --SubDomain blog \
--RecordType CNAME --RecordLine "默认" \
--Value "blog.example.com.eo.dnse2.com" --TTL 600
4. 配 URL 重写(关键,见坑2)
5. 申请 + 绑定免费证书(见坑5)
6. 验证
踩坑实录(核心价值)
以下每个坑都是真实踩过、花了时间排查的。
坑1:回源 Host 必须是 COS 桶域名
EdgeOne 回源时,HostHeader 默认是加速域名(blog.example.com),但 COS 靠 Host 路由到桶——Host 错了 COS 返回 400。
解决:建加速域名时显式设 HostHeader 为 COS 桶域名(<bucket>.cos.<region>.myqcloud.com)。好消息是 COS 桶域名的 Host 和 SNI 天然一致,不像 GitHub Pages 那样有 Host/SNI 分离的坑。
坑2:COS 根路径 / 返回 403
访客访问 blog.example.com/,EdgeOne 回源 COS 的 /,而 COS 桶根没有对象,且 public-read 不允许列桶 → 403。
解决:配 EdgeOne 回源 URL 重写 / → /index.html。这其实是所有"对象存储托管静态站"的通病。
坑3:通配符 DNS 记录的缓存
如果域名有 * 通配符 A 记录(很多人用来兜底),新加的精确 CNAME 在生效前会被通配符兜底(返回旧 IP)。
排查:直接查权威 NS 的 CNAME 类型,能确认数据已正确(dig @权威NS <sub> CNAME),只是递归解析器缓存未过期。
解决:等 TTL 过期;或验证时用 curl --resolve <domain>:443:<EdgeOne IP> 绕过缓存立即测试。
坑4:EdgeOne 调度域名 dig 返回 SERVFAIL 是正常的
EdgeOne 给的 CNAME 目标形如 *.eo.dnse2.com,直接 dig 它会返回 SERVFAIL。别误判为配置错误——它只对 CNAME 跟随有效(作为 CNAME 目标被解析时正常返回边缘 IP)。
坑5:免费证书签发后不会自动部署
ApplyFreeCertificate 签发证书后,域名的 Certificate.Mode 默认是 disable,边缘会返回默认的 *.cdn.myqcloud.com 证书,导致 TLS 域名不匹配。
解决:必须手动绑定:
tccli teo ModifyHostsCertificate --ZoneId <zone-id> \
--Hosts '["blog.example.com"]' --Mode eofreecert
绑定后约 30 秒~几分钟部署到边缘。
坑6:tccli 规则引擎 API 的两个坑
配 URL 重写规则时:
CreateL7AccRules --Rules '<json>'直接传 JSON 会触发 tccli 递归 bug,必须用--cli-input-json file://传文件。- Condition 表达式:连接符是
and不是&&;路径匹配变量名不固定(http.request.path、http.request.uri都被拒),优先用 regex 在动作里做路径精确匹配,而不是在 Condition 里匹配路径。
坑7:UpstreamURLRewrite 的 schema 试出来的
回源 URL 重写的 Action 合法值(API 报错才会列出):
[replace, addPrefix, rmvPrefix, regexReplace]
regexReplace 时字段:Regex 放外层(不是嵌套在 Path 对象里),配 Value。
坑8:目录索引的反向引用语法
Hugo 这类多页静态站,每个目录都有 index.html(如 /posts/xxx/index.html)。访问 /posts/xxx/ 要重写到 /posts/xxx/index.html。用 regexReplace:
{"Regex": "^(.*/)$", "Value": "$1index.html"}
关键:反向引用必须用 $1,不能用 ${1}——后者会被 EdgeOne 误判为"预设变量"报错。
坑9:EdgeOne 会缓存 404
规则生效前回源失败的 404 会被边缘缓存。规则改好后必须 purge 那些目录 URL,否则持续返回旧 404。
让 AI 读到纯 Markdown(Hugo 配置)
这是整个项目最有意思的部分。Hugo 有 markdown output format,能让每篇文章同时输出 HTML(人看)和原始 .md(AI 看)。
配置
hugo.toml:
[outputFormats.markdown]
mediaType = "text/markdown"
isPlainText = true
baseName = "index"
suffix = "md"
[outputs]
page = ["HTML", "markdown"]
模板(最大的坑)
isPlainText=true 的 output format,模板文件用 .md 扩展名(不是 .html):
layouts/_default/single.markdown.md
内容就一行:
{{ .RawContent }}
输出的是纯正文 Markdown(不含 frontmatter)。
三个 Hugo 坑
- 文章 date 不能是未来:UTC 时差导致本地今天写的文章可能被判为未来,Hugo 默认不渲染。date 设为当天或之前,或加
buildFuture = true。 - 模板扩展名是
.md:single.markdown.html不被识别(前面试了好久),single.markdown.md才对。 - COS 对 .md 默认 Content-Type 是
application/octet-stream(当二进制)!AI 读到可能不当文本。必须覆盖上传时设text/markdown,且sync_upload不按扩展名区分,要对 .md 单独 upload。
最终效果
每篇文章两个地址:
/posts/xxx/→ 渲染 HTML(人看)/posts/xxx/index.md→ 纯 Markdown(AI 读,Content-Type: text/markdown)
成本实测
| 项目 | 单价 | 实际 |
|---|---|---|
| COS 存储 | 0.118 元/GB/月 | 几 MB → 约 0.001 元 |
| CDN 回源流量 | 0.15 元/GB | 有缓存命中,极少 |
| EdgeOne 边缘流量 | 走套餐 | 个人版套餐内 |
| 合计 | < 0.2 元/月 |
注意:EdgeOne 回源 COS 不是免费内网。腾讯云"同地域内网免费"规则明确除外 CDN/EdgeOne,走的是单独的 CDN 回源流量计费项(0.15 元/GB)。但比外网下行(0.50)便宜 70%,且静态站缓存命中率高,实际成本可忽略。
日常使用
写完一篇一键部署(脚本自动处理所有坑):
cd ~/ai-blog && ./deploy.sh 新文章名
脚本自动:Hugo build → 上传 COS → 修正 .md 的 Content-Type → purge 缓存。然后把输出的链接 https://blog.example.com/posts/新文章名/index.md 丢给 AI 即可。
总结
- 技术上:COS + EdgeOne 搭国内加速静态站完全可行,成本近零,但坑集中在"回源 Host、根路径 403、URL 重写 schema、证书绑定、Content-Type"这几处。
- 给 AI 看 md:Hugo 的 markdown output format 能做到,关键是模板扩展名(
.md)和 COS Content-Type(text/markdown)。 - 最大的教训:EdgeOne 的规则引擎 API schema 不够透明,很多字段值要靠 API 报错反向猜;把这些固化成文档/脚本后,后续部署就是无脑一条命令。
如果你也在搭类似的东西,希望这篇能帮你省掉几个小时的踩坑时间。