使用 Cloudflare R2 + Workers 构建稳定、高性能的软件下载分发
本文记录了在实际项目中使用 Cloudflare R2 + Workers 构建软件下载分发体系时,从最初设计、踩坑、排错,到最终稳定方案的完整过程。内容包含 Cache Rules 与 Worker 缓存冲突的真实问题,适合直接作为生产级参考。
一、背景与目标
在为 AirTools 构建桌面端分发体系时,我希望实现:
- 安装包(
.exe/.dmg等)全球 CDN 强缓存 - 版本元信息(
latest.json)实时更新、不被缓存 - 不自建服务器,尽量使用 Cloudflare 原生能力
- 缓存行为稳定、可预测,不出现随机 500
最终技术选型为 Cloudflare R2 + Cloudflare Workers。
二、初始架构设计
目标结构如下:
1 | Client |
逻辑上并不复杂,但在实际落地过程中,问题主要集中在 缓存控制细节 上。
三、最早遇到的异常现象
1️⃣ 缓存始终不生效
即便返回了:
1 | Cache-Control: public, max-age=2592000, immutable |
响应中仍反复出现:
1 | cf-cache-status: DYNAMIC |
2️⃣ 同一个 URL,返回结果不一致
对同一下载地址多次执行:
1 | curl -I https://dl.airtools.app/v0.1.19/AirTools_0.1.19_x64-setup.exe |
会出现:
- 有时
200 - 有时
500 - 缓存状态随机变化
这在下载分发场景中是非常危险的信号。
3️⃣ Cloudflare Dashboard 偶发 502
在 Workers / Builds 页面中,Cloudflare 控制台本身也开始报:
1 | API Request Failed (502) |
这通常意味着某个 Worker 在边缘节点频繁返回 5xx。
四、Cache Rules 带来的隐藏问题(关键)
在排查过程中,一个非常容易被忽略的问题逐渐浮现:Cache Rules 与 Worker 的缓存逻辑发生了冲突。
1️⃣ Cache Rules 的“误导性描述”
Cloudflare Cache Rules 中常见的选项包括:
- 缓存资格(Eligible for cache)
- 绕过缓存(Bypass cache)
这些规则的描述很容易让人误以为:
“只要标记为符合缓存条件,Cloudflare 就一定会缓存”
这是不准确的。
Cache Rules 只是在「是否允许缓存」这一层做判断,真正是否缓存,仍然取决于:
- Worker 中是否使用
cacheEverything - 返回的 HTTP 状态码是否为 200
- 实际响应头中的
Cache-Control
2️⃣ Cache Rules 覆盖 Worker Header
在早期配置中,我曾在 Cache Rules 中尝试:
- 针对
.exe / .dmg设置缓存 - 或统一修改
Cache-Control
结果是:
- Worker 返回的
Cache-Control被规则覆盖 - 错误响应(500)也被错误地标记为可缓存候选
- 实际缓存行为变得不可预测
结论:当请求经过 Worker 时,Cache Rules 很容易成为“干扰项”。
3️⃣ 最终选择:让 Worker 成为唯一缓存决策点
在最终方案中:
- 不再依赖 Cache Rules 控制下载缓存
- Cache Rules 保持最小化甚至删除
- 所有缓存判断统一在 Worker 中完成
这样可以保证:
缓存逻辑是可读的、可调试的、可预测的。
五、真正的根因总结
综合排查后,问题根因集中在三点:
- Worker 未处理回源失败,导致 500 被返回
- 500 响应参与缓存资格判断,污染后续请求
- Cache Rules 与 Worker 缓存逻辑发生冲突
六、最终稳定方案设计
核心原则只有一句话:
只缓存成功的安装包,失败请求永不进入缓存路径
缓存策略表
| 类型 | 是否缓存 | 说明 |
|---|---|---|
.exe / .dmg / .zip |
✅ | 强缓存 30 天 |
latest.json |
❌ | 永远回源 |
| 500 / 502 | ❌ | 禁止缓存 |
七、最终 Worker 实现(生产可用)
1 | export default { |
八、最终验证结果
latest.jsoncf-cache-status: DYNAMIC- 实时回源
安装包下载
cf-cache-status: HITage持续增长- 支持断点续传
九、经验总结
- 不要缓存失败响应
- Worker 必须处理
fetch异常 - Cache Rules 不适合与复杂 Worker 缓存逻辑混用
- 一个域名只承担一种角色(Worker 或 R2)
- 判断缓存是否生效,以
cf-cache-status为准
十、结语
Cloudflare R2 + Workers 非常适合用于软件下载分发,但稳定性来自于细节,而不是默认配置。
只要把缓存决策收敛到 Worker 内部,并明确区分“可缓存内容”和“实时内容”,这套架构可以长期稳定运行,且维护成本极低。