使用Cloudflare R2对象存储作为网站图床

写文章总要放图。截图、封面一张张提交进代码仓库,仓库会越来越大;放在托管平台上,又要担心流量配额,一张高清截图可能比整个站点的页面代码加起来还大。所以我给这个站单独配了一个图床。

我挑图床看三件事:存储和流量便宜,访问快,能绑自己的域名。Cloudflare R2 三样都满足,最关键的是出口流量免费。图床是写一次、读很多次的场景,流量不要钱,成本就只剩存储,每 GB 每月 0.015 美元。它还兼容 S3 的接口,现成的 S3 工具基本都能直接用。

这篇记录控制台这一侧的配置:存储桶、访问域名和 CORS。代码那一侧(上传接口和图片的清理)写在下一篇。

创建存储桶

在 Cloudflare 控制台进入 R2,点「创建存储桶」:

屏幕截图 2025-08-20 104339.png

屏幕截图 2025-08-20 104428.png

建好之后可以直接在控制台拖文件上传。我先手动传了几张,确认访问路径没问题:

屏幕截图 2025-08-20 104856.png

访问域名

存储桶里的文件需要一个公开地址。可以用 Cloudflare 提供的公共开发地址,也可以绑自己的子域名。我绑的是 images.greatpi.dev:地址在自己手里,以后就算换平台,只要域名还在,文章里的图片链接就不会失效。

image.png

CORS:大多数情况用不上

网页上用 <img> 显示图片,不需要配 CORS。只有要用 JavaScript 读取图片内容的时候才需要,比如把图片画进 Canvas 做裁剪或压缩,或者让浏览器直接把文件传到 R2。

我的站两种都没有:上传是先传到自己的服务端接口,再由服务端写进 R2。所以 CORS 我只放行了读取,来源限定为本地开发和自己的域名:

[
  {
    "AllowedOrigins": ["http://localhost:3000", "https://*.greatpi.dev"],
    "AllowedMethods": ["GET"],
    "AllowedHeaders": ["*"],
    "MaxAgeSeconds": 3600
  }
]

image.png

以后要做浏览器直传,再把 PUT 加进去。

防盗链:对 R2 来说意义不大

站点以前用的是 greatqian.me 这个域名,那时候我在 Cloudflare 上配过防盗链(Hotlink Protection)。迁到 greatpi.dev 之后,这条规则没有再配。

回头想想,对 R2 图床来说,防盗链的意义本来就不大。防盗链主要是为了省流量费,而 R2 的出口流量是免费的,别人引用我的图,并不会让我多付钱。图片走的是自定义域名,经过 Cloudflare 的缓存,重复的请求大多也到不了 R2 本身。真到了有人大量盗用的那一天,再加规则也来得及。

接进编辑器

控制台配好之后,剩下的都在代码里:站内加一个上传接口,收到图片写进 R2,返回公开地址;编辑器上传图片时调用它,拿到地址后自动插入一行 ![描述](url)。这篇文章里的截图就是这么进来的。

最开始我用 AWS 的 S3 SDK 往 R2 里写,部署到 Cloudflare 之后却崩了,后来换成了 aws4fetch。原因和完整的代码写在下一篇里。


配好之后这套图床基本不用再管。从 2025 年 8 月用到现在,一共存了几十张图,离 R2 每月 10GB 的免费存储额度还差得很远。

更多