写在前面: 如果未来的某一天,你再次发现明明已经从存储桶里永久删除的图片,竟然还能在浏览器里正常访问,甚至 Cloudflare 缓存怎么清都清不掉。 请冷静,不要怀疑 CDN 出了 Bug,也不要觉得遇到了灵异事件。 回来看这篇笔记,凶手就是当初那个“过于贴心”的开源项目代码。

👻 灵异现象:图片“复活”了

事情的起因很简单:我基于开源项目 MarSeventh/CloudFlare-ImgBed 搭建了自己的图床,后端存储使用的是 Cloudflare R2

某天我决定重构图床,于是清空了 R2 存储桶,甚至删除了整个项目重新部署。按理说,旧的图片链接应该全部变红(404 Not Found)。

然而,诡异的事情发生了:

  1. 访问旧图片链接:图片依然正常加载,毫发无损。
  2. 检查 R2 桶:里面空空如也,根本没有这个文件。
  3. 检查 KV 数据库:也是空的,没有记录。
  4. CDN 缓存:我在 Cloudflare 后台点击了 Purge All Cache(清除所有缓存)。
  5. 结果:清除缓存后,第一次访问短暂变慢(回源),紧接着控制台再次显示 CF-Cache-Status: HIT

一张在物理层面已经被“销毁”的图片,为什么会像幽灵一样,每次清除缓存后都能原地复活?

1. 项目的前世今生

CloudFlare-ImgBed 这个项目,最早其实是基于 Telegra.ph(Telegram 的匿名发布平台)做的。

  • 早期逻辑:用户上传 -> Worker 转发给 Telegra.ph -> 返回 Telegra.ph 的图片链接。
  • 后期升级:Cloudflare 推出了 R2 存储,作者为了稳定性,增加了 R2 支持,把 R2 作为首选存储。

2. “Telegra.ph禁用免费的接口

以前上传到 Telegraph 的图片不受影响,现在无法再白嫖了,为了避免已经上传的图片无法使用,代码底层逻辑会继续try get Telegra.ph的图片。

3. 真相大白

这就是“缓存复活”的完整链条:

  1. 我点了“清除缓存” -> 边缘节点缓存清空。

  2. 我访问图片 -> 边缘节点发现没缓存 -> 请求回源给 Worker

  3. Worker 去 R2 查 -> 发现文件没了(因为我删了)。

  4. Worker 触发兜底逻辑 -> 偷偷去 telegra.ph 请求同名文件。

  5. 由于 Telegra.ph 的图片是永久存储且无法删除的 -> Worker 拿到了图片!

  6. Worker 把图片传回给边缘节点 -> 边缘节点重新缓存该图片 -> 用户看到图片。

既然找到了原因,解决办法就很简单。我不希望我的图床和不可控的 Telegra.ph 藕断丝连,我要它变成一个纯粹的 R2 图床。

修改 Worker 代码:

找到类似 fetch('https://telegra.ph/file/' + ...) 的代码段,将其删除或注释,改为直接返回 404。

JavaScript

1
2
3
4
5
6
7
8
9
// 修改后的逻辑
const file = await env.R2_BUCKET.get(key);

if (!file) {
    // R2 没找到?那就是没了,别去别处找了
    return new Response("Image Not Found in R2.", { status: 404 });
}

return new Response(file.body, ...);

修改部署后,再次清除 CDN 缓存。这一次,幽灵图片终于彻底消失了,返回了令我安心的 404 Not Found