两站点 Cloudflare + 免费 R2 + 优选域名加速:一次把坑踩明白的复盘

一场关于图片加速的连续剧:我们如何把两个站点从“能访问”修到“真的快”

先说结论:

  • d3vaiya 现在都能稳定走 Cloudflare + R2 链路。
  • 旧图、新图都能打开。
  • 本地历史上传可备份后清理,不再被磁盘绑架。

再说过程:

第一幕:以为是网络问题,结果是“系统性绕路”

一开始大家都盯着延迟看:

  • 中国到新加坡,ping 看起来 300ms。
  • SSH 卡到怀疑人生。
  • 业务站点因为用了 CF 反代和优选域名,体感还行。

于是灵魂一问就来了:

“SSH 能不能也这样加速?”

答案是:能折腾,但不是你给 HTTP 站点加速那套就能直接平移。后面我们把重点先收敛到图片链路,因为这才是用户可感知的大头。

第二幕:404 不是对象没了,而是回源回错家了

最经典的一坑:

  • 链接 uploads.aiya.de5.net/... 打开是 404
  • 但去 R2 里 head_object,对象明明在。

这说明什么?

说明“存储层没问题,流量层有问题”。

最终定位:

  • uploads.aiya 的 SaaS 自定义主机,源站一度指到了另一个桶的入口。
  • 回源 Host/SNI 和 Nginx 匹配还出现过错位。
  • 再叠加 Cloudflare 把旧 404 缓存住,看起来就像“修了也没修”。

一句话总结:

对象在桶里,不等于用户能拿到。

第三幕:你以为是 DNS,实际上是“多层配置联动”

这次链路其实有四层:

  1. 华为云分线路由(国内/境外)
  2. Cloudflare SaaS custom hostname
  3. Nginx 回源编排
  4. R2 bucket 的 public/managed/custom domain

任何一层“看起来差不多”的配置,都可能把请求导到错误的桶或错误的 Host。

所以我们的修法不是“拍脑袋改一条”,而是:

  • 先做备份。
  • 再逐层验证(DNS → SaaS → origin → bucket object)。
  • 每层都用 curl -I 和对象 HEAD 做事实校验。

第四幕:为什么老帖总爱“看起来没改”

很多人会在这里崩溃:

“我不是都上 S3/R2 了吗?为什么老帖还是 aiya.de5.net/uploads/...?”

因为 Discourse 有两个世界:

  • raw:帖子原文(常见 upload://...
  • cooked:渲染后的 HTML(img/src/srcset 在这里)

posts:rebake 会用 raw 重算 cooked

所以如果你只改了 cooked,后续 rebake 可能又给你“打回默认写法”。

这不是 R2 失效,是渲染策略的正常行为。

第五幕:这次我们到底做了什么

这次最终落地是:

  • 修正 uploads.aiya 回源到正确 bucket。
  • 清理旧 404 缓存。
  • 同步历史缺失对象到 R2。
  • 对老帖 cooked 批量替换到 https://uploads.aiya.de5.net/...(让展示也统一)。

效果:

  • 你贴出来的炸图全部恢复。
  • 新图、旧图都能命中加速链路。

给后来者的“少走弯路清单”

  1. 不要先怀疑对象丢失,先做 HEAD object
  2. 404 先看缓存状态:cf-cache-status: HIT 可能只是旧缓存。
  3. SaaS custom hostname 改源后,务必验证实际 Host 匹配。
  4. 一次改一层,层层验证,不要多处同时改然后祈祷。
  5. 先备份再迁移,尤其是 /shared/uploads/*
  6. 把“回滚路径”写出来,比“这次终于成功”更重要。

彩蛋:这次最真实的体感

这不是“一个 DNS 记录没填对”的问题。

这是“免费方案拼装出来的半企业架构”:

  • 能省钱,能提速,能跑起来;
  • 但你要拿出 SRE 的耐心,接受它会在边界条件里教育你。

折腾归折腾,最终是值得的:

  • 成本没爆;
  • 速度上来了;
  • 架构也终于从“能用”变成“可解释、可维护”。

希望这篇能帮后来者少熬几个夜。