























起因是刷到一个在线摄影集项目 Afilmory ,被其美观干净的 UI 所吸引,所以也想部署一个自己的摄影集网站,虽然买相机至今还没拍摄多少照片……
本着能白嫖就白嫖的原则,使用 Cloudfare R2 作为照片存储桶,Vercel 来部署静态网站。部署项目采用的是本地图片预处理 + 远程静态资源使用的方式,因为项目在构建时需要处理大量图片,这是一个非常消耗计算资源(CPU 和内存)的过程。Vercel 的免费套餐构建环境有时间和内存限制,如果照片数量很多,构建过程很可能会失败。下面就着重讲述一下使用 Cloudfare R2 存储照片、使用并出现问题的过程。
Cloudflare R2 是一款对象存储服务,旨在让开发者能够存储大量非结构化数据,其最核心的特点是免除了其他云存储服务商通常会收取的高昂的出口费用。
R2 提供了一个非常可观的免费套餐,尤其适合个人开发者和小型项目:

📢
建议绑定一个自定义域名,因为 cloudfare 提供的域名有一定的限制:
This URL is rate-limited and not recommended for production. Cloudflare features like Access and Caching are unavailable. Connect a custom domain to the bucket to support production workloads.
绑定自定义域名需要将域名托管到 cloudfare 上,步骤也很简单,按照提供的信息更换原来域名的域名服务器为 Cloudflare 的域名服务器。同时迁移成功后,所有和域名解析相关的操作都必须在 Cloudflare 的 DNS 控制台进行,原来域名提供商的 DNS 解析设置将不再生效。
CORS 的全称是 “跨域资源共享” (Cross-Origin Resource Sharing)。它是一种基于 HTTP 头的机制,允许服务器声明,除了它自己的源(域、协议或端口)之外,还有哪些源可以有权限访问它加载的资源。
要理解 CORS,首先必须理解浏览器的 “同源策略” (Same-Origin Policy)。同源策略是浏览器最核心、最基本的安全功能。它规定,一个源的文档或脚本,只能与和它 “同源” 的资源进行交互。
如果两个 URL 的 协议 (protocol)、主机 (host) 和 端口 (port) 都相同,那么它们就是同源的。
URL | 与 | 结果 |
| 同源 | 允许 |
| 不同协议 (https vs http) | 跨域 |
| 不同主机 (www.example.com vs example.com) | 跨域 |
| 不同端口 (8080 vs 80) | 跨域 |
同源策略极大地提高了浏览器的安全性,可以有效防止恶意网站通过脚本轻易地窃取你在其他网站上的数据。但是,跨域请求(比如从 site-a.com 的前端页面请求 api.b.com 的数据)在某些情况下是合理且常见的需求。为了在不完全破坏同源策略安全性的前提下,有控制地允许这些跨域请求,CORS 应运而生。
CORS 的核心思想是:使用新增的 HTTP 头部,让服务器告诉浏览器,它是否允许当前这个源的请求。 整个过程由浏览器自动完成,对前端开发者来说通常是无感的。
Origin 字段,表明请求来自哪个源。Origin 字段。如果这个源在许可范围内,服务器就会在响应头中加入 Access-Control-Allow-Origin 字段。Access-Control-Allow-Origin 字段。如果该字段的值是请求的源,则请求成功,浏览器将响应内容交给 JavaScript。否则,浏览器会拦截响应,并在控制台抛出 CORS 错误。📢
CORS 完全由服务器端控制, 解决 CORS 问题的关键在于正确配置服务器的响应头。
构建好站点之后,如果在没有配置 Cloudfare R2 的 CORS 策略情况下,直接打开图片,会得到对应的报错:

打开 Cloudfare R2 并进入创建的存储图,比如我的是 huhu-photo,在 Settings 菜单栏下就有 CORS 配置界面,可以设置对应的 CORS 策略:

我设置的策略如下,并没有限制特定的域名请求:
"AllowedOrigins":告诉 R2 允许哪些源(域名)来访问资源,这里是允许公开请求。"AllowedMethods": 允许的 HTTP 请求方法,对于图片对象来说,GET 、HEAD 和 OPTIONS 一般就足够了。⚠️
问题来了,即使我设置了 CORS 策略,使用了无痕浏览(排除缓存的问题),但是在查看某些图片时依然遭到了拒绝,显示同样的 CORS 错误,而且这些出错的图片不是固定的,同时这是为什么呢?
询问 AI 之后,尝试使用 curl 验证一下请求返回的 header 部分,来确定 CORS 策略是否正确生效:
可以看到响应部分确实有 access-control-allow-origin 字段,排除了 CORS 策略的问题。查不到具体的问题,只能去提交个 issue 了,果然 issue 是最好的学习地方,有个佬遇到了同样的问题,并给出了具体的原因,以及完整的复现流程 🤩,具体内容在这里。
💡
源服务器 R2 只在收到带有 Origin 请求头的请求时,才会在响应里加上 CORS 相关的头部(比如 Access-Control-Allow-Origin)。
接下来具体阐述一下佬友给出的问题原因和解决方案:
Access-Control-Allow-Origin)。Cloudflare 拿到这张不带 CORS 头的图片后,于是就把这张图缓存到了自己的服务器上。gallery.mwwlzz.top)再次请求这张图片时,这次请求是一个明确的跨域请求,浏览器会带上 Origin 头。请求发到 Cloudflare。📢
源服务器 R2 只在收到带有 Origin 请求头的请求时,才会在响应里加上 CORS 相关的头部(比如 Access-Control-Allow-Origin)。
这就是为什么问题会随机出现:能否加载成功,取决于请求的那张图片在 Cloudflare 的缓存里是带 CORS 头的版本还是不带 CORS 头的版本。刷新页面可能会清除或替换缓存,导致加载情况发生变化。
❓
既然浏览器会自动加上 Origin 头,那为什么会没有触发 CORS 呢?哪种情况可能没有带上 Origin 头呢?
hu-r2.mwwlzz.top。Origin 请求头,告诉服务器这个请求来自哪里。Origin: https://gallery.mwwlzz.topOrigin 头的请求。Origin 请求头。https://gallery.mwwlzz.top 是被允许的。Access-Control-Allow-Origin: https://gallery.mwwlzz.top。这种情况可能在多种情况下发生,最典型的一个例子就是:
⚠️
直接在浏览器地址栏里输入了图片地址 https://hu-r2.mwwlzz.top/image.jpg 并查看图片。(我好像确实干了)
hu-r2.mwwlzz.top)。Origin 这个请求头。Origin 头的请求。Origin 请求头。Access-Control-Allow-Origin。现在,Cloudflare 的缓存里有了一份不带 CORS 头部的图片。之后,当通过网站再去请求这张已经被缓存的图片时:
Origin 头的跨域请求。Access-Control-Allow-Origin 头部,于是立刻拒绝加载图片,并在控制台报错。Cloudflare 本身是无辜的,它只是一个高效的缓存系统,忠实地缓存从源服务器 R2 收到的东西。问题的根源在于,它缓存了由一个“非跨域请求”所产生的“不带 CORS 头的响应”,然后错误地将这份缓存提供给了一个需要 CORS 头的“跨域请求”。
解决方案的核心就是告诉 Cloudflare:凡是访问 R2 图片域名的请求,都不要缓存,每次都去 R2 源服务器拿。 这样就能保证每次请求返回的都是 R2 直接给出的、带有正确 CORS 策略的响应。因此需要创建一个 “缓存规则 (Cache Rule)” :
mwwlzz.topBypass Cache for R2。hu-r2.mwwlzz.top
规则创建后会立即生效。现在所有访问 hu-r2.mwwlzz.top 的请求都会绕过 Cloudflare 的 CDN 缓存,直接由 R2 处理,按理说彻底解决了间歇性的 CORS 问题。但是,还是会出现之前的问题 🥲。
接着尝试清楚所有的缓存,同样是在 “Caching” 菜单栏下,找到 “Configuration”,然后点击 “Purge Everything”,即可清除所有的 cloudfare 缓存内容。但,还是不行。

由于上述方法都不行,最后尝试部署一个 worker,拦截所有发往 R2 域名的响应,然后强行给这些响应加上正确的 CORS 头部,最后再发给浏览器。这样一来,无论 R2 返回的响应是否带 CORS 头,也无论 Cloudflare 的缓存里是什么内容,Worker 都会确保最终浏览器收到的响应一定是带有正确 CORS 头的。
这段代码会做两件事:
OPTIONS 预检请求,它会直接返回正确的 CORS 头部,告诉浏览器“安全,请继续”。GET 或 HEAD 等实际的图片请求,它会先从 R2(或缓存)获取图片,然后强行在响应中添加上正确的 CORS 头部再返回给浏览器。设置路由来触发 Worker:
Worker 创建并部署好代码后,它还不知道要对哪些请求生效。我们需要告诉它。找到 Worker 的管理页面:
hu-r2.mwwlzz.top/*mwwlzz.top。完成以上所有步骤后,这个 Worker 就会自动修改所有对 R2 资源的请求,强行保证 CORS 头部正确无误。至此,也确实彻底解决了之前的问题!
想记录这次解决问题的过程,是因为感触良多。AI 能成为高效的“执行者”,却难成深刻的“洞察者”。当面对复杂未知的问题时,它的能力边界就显现出来。而人的智慧和经验与社区的公开,恰好能弥补这最后的“一公里”。一个有经验的开发者,能根据过往的经验找出问题的关键,这种需要实际工程的经验积累,是 AI 难以复制的。而社区打破了个人认知的孤岛,让分散的智慧得以汇聚和分享。
最后也是得到了好看的 gallery,欢迎浏览我这还较为青涩的摄影:https://gallery.mwwlzz.top/

此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。