用腾讯云 COS + EdgeOne 搭建「AI 可读」的 Markdown 博客

折腾了一天,把一个静态博客从零搭起来,部署到已备案域名、国内加速,而且每篇文章都有一个纯 Markdown 链接可以直接丢给 AI 读。这篇文章记录完整过程和踩过的坑,希望能帮人少走弯路。

目标

方案选型

对比了三种方案:

方案回源国内速度备注
GitHub Pages + EdgeOne 加速有源站(GitHub 美国),未命中要跨洋回源一般回源配置复杂(Host/SNI 分离坑)
EdgeOne Pages 直发无源站较新产品,国内加速需确认免费版
COS + EdgeOneCOS 作源站,同生态成本近零,配置成熟 ← 选这个

最终选 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 重写规则时:

  1. CreateL7AccRules --Rules '<json>' 直接传 JSON 会触发 tccli 递归 bug,必须用 --cli-input-json file:// 传文件。
  2. Condition 表达式:连接符是 and 不是 &&;路径匹配变量名不固定(http.request.pathhttp.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 坑

  1. 文章 date 不能是未来:UTC 时差导致本地今天写的文章可能被判为未来,Hugo 默认不渲染。date 设为当天或之前,或加 buildFuture = true
  2. 模板扩展名是 .mdsingle.markdown.html 不被识别(前面试了好久),single.markdown.md 才对。
  3. COS 对 .md 默认 Content-Type 是 application/octet-stream(当二进制)!AI 读到可能不当文本。必须覆盖上传时设 text/markdown,且 sync_upload 不按扩展名区分,要对 .md 单独 upload。

最终效果

每篇文章两个地址:

成本实测

项目单价实际
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 即可。

总结

如果你也在搭类似的东西,希望这篇能帮你省掉几个小时的踩坑时间。

← 返回首页