结论先给:R2 依然是目前个人图床里性价比最高的选择,免费额度够用、出站不收流量费、不用担心小厂跑路。但它并不是「注册完就能传图」,中间有两道门槛,跨过去之后才叫一劳永逸。

一、为什么是 R2
把候选方案过一遍,理由其实只有三条:
- 额度实在:每月 10 GB 存储、100 万次写、1000 万次读,个人博客很难跑满;
- 出站不计费:这条最容易被忽略,却是图床场景的关键——图片被反复读取,流量费才是真成本;
- S3 协议兼容:几乎一切工具都能接,PicGo、Halo、rclone、自家脚本都行。
代价是它长得不像图床:没有中文后台,没有「复制链接」按钮,一切都得自己拼参数。所以下面这几步,顺序不能乱。
二、开通:从账号到能上传
- 登录 Cloudflare,进入左侧「R2 对象存储」,按提示订阅 R2(这一步要绑付款方式,信用卡或国区 PayPal 都行);
- 创建存储桶,名称自取,位置选离读者近的——面向中文读者就选亚太;
- 进入桶的「设置」,找到
r2.dev子域,允许访问(输入allow确认); - 回到「对象」页,拖一张图上去,点开它,复制生成的网址试试。
到这一步,外网能打开、国内大概率打不开。这不是配置错了,是 r2.dev 这个默认域名在国内的连通性本来就不稳,官方也明确不建议把它用于生产。于是进入第三关。
三、域名:绕不过去的那道坎
解法是把自有域名托管到 Cloudflare,再把它绑到桶上,让图片走 Cloudflare 的 CDN。
- Cloudflare 左侧「域」→ 填入域名,套餐选免费;
- 在激活页复制两个名称服务器(NS);
- 去域名注册商那里把 NS 改掉;
- 等状态从「待处理」变成「活动」;
- 回到 R2 → 桶 → 设置 → 自定义域 → 连接域。
绑好之后,同一个文件会多出一条 https://img.你的域名/路径 的链接。这条链接才是能长期用的那条。
四、密钥:给程序用的那一对
图床工具不认你的账号密码,只认 S3 密钥。在 R2 概览页点「API → 管理 API 令牌」,新建令牌时权限只给「对象读和写」,范围限定到具体那个桶。拿到三样东西:Access Key ID、Secret Access Key、以及账户的 S3 端点。
用 PicGo 的话,装 S3 插件后填这张表:
| 参数 | 填什么 |
|---|---|
| 密钥 ID / 密钥 | 上一步生成的那一对 |
| 存储桶 | 你的桶名 |
| 自定义节点 | https://<账户ID>.r2.cloudflarestorage.com |
| 地区 | auto |
| 文件路径 | picgo/{全文件名} |
| 自定义域名 | https://img.你的域名(要带协议头) |
五、验证:别看界面,看返回值
后台显示「已连接」不算证据。我习惯用三条独立证据收尾:
- 列得出对象:用 S3 客户端列一次桶,返回 200 且条数对得上;
- 写得进去:上传一个小文件,再删掉;
- 公网打得开:拿图片链接直接请求,状态 200、类型正确、字节数和后台一致。
顺手看一眼响应头里的 cf-cache-status:出现 HIT,说明图片已经在 Cloudflare 边缘缓存里,读者访问不会每次回源。
六、如果你也用 Halo,有更短的路
PicGo 适合「站外单张图、批量传、要 Markdown 链接」的场景。如果你是 Halo 用户,其实可以跳过它:
- 装官方插件「对象存储(Amazon S3 协议)」;
- 在「附件 → 存储策略」新建一个 S3 策略,把上面那套参数填进去;
- 把它设为默认。
之后在编辑器里插图,文件会直接落到 R2,链接就是你的自定义域。注意插件版本:新版对 Halo 版本有要求,装之前先对一下,不要盲目上最新。
另外要清楚:切换存储策略只影响新上传。历史附件还在服务器本地,要迁移得单独做。
七、成本、缓存与防盗链:跑通之后的样子
免费额度之外的单价很低(存储约每 GB 每月一角钱量级,以官网为准)。真正决定体验的是缓存和防盗链这两件事,下面是我实际跑通的配置和回测数字。
1. 防盗链:一个开关,或者一条规则
最省事的是打开 Scrape Shield 里的热链接保护:
Cloudflare → 你的域名 → 安全性 → 设置 → 客户端滥用 → 热链接保护 → 开
它的行为是:Referer 为空放行;Referer 非空且不是本站 → 403。想要更精细(放行搜索引擎、放行指定 UA),就改用 WAF 自定义规则,表达式写:
(http.host eq "img.wycha.top" and http.referer ne "" and not http.referer contains "wycha.top")动作选 Block。两条路效果一样,前者一键、后者可加白名单。
2. 缓存:一条 Cache Rule 只管图片域名
在 缓存 → Cache Rules 里建一条:
- 表达式:
http.host eq "img.wycha.top" - 边缘 TTL:30 天,模式选覆盖源站
- 浏览器 TTL:1 天,同样覆盖源站
用 API 建出来长这样,关键是 action_parameters:
{
"action": "set_cache_settings",
"expression": "(http.host eq \"img.wycha.top\")",
"action_parameters": {
"cache": true,
"edge_ttl": { "mode": "override_origin", "default": 2592000 },
"browser_ttl": { "mode": "override_origin", "default": 86400 }
}
}把规则限定在图片 host 上是有意的:HTML 页面不能被这条规则波及,否则文章更新后读者看到的是旧页面。
3. 回测:四条证据
配置完之后我跑了四组请求,结果如下:
| 请求 | 结果 |
|---|---|
| 图片(已缓存) | 200,max-age=86400,cf-cache-status: HIT,age: 2009 |
| 图片(新缓存键) | 200,max-age=86400,首次 MISS,之后命中 |
| 文章页 HTML | 200,no-cache, no-store,未被规则波及 |
| 图片 + 外站 Referer | 403,被边缘拦下 |
有两个细节要注意:
- 必须放行空 Referer。微信编辑器抓图、Telegram/Slack 预览、RSS 阅读器、读者直接把链接粘到地址栏,全是空 Referer;规则写成
referer ne ""就是在保护这条。 - 边缘 TTL 不会出现在响应头里。能看到的只有
max-age和cf-cache-status,想确认 30 天边缘缓存,得看 Cache 分析或 Rules 的命中统计。
一句提醒:大文件不要放在自建图床上。视频动辄几百兆,既拖慢首屏,也容易让边缘缓存失效、把回源打成常态。视频走 B 站之类的平台外链,图片才是这套方案的主场。
结语
整套方案的真正价值不在「免费」,而在所有权:域名是你的、桶是你的、链接格式由你定,换工具不用换链接。做完之后你会得到一条稳定的图片域名,和一套随时能复制到下一个项目的参数。
如果只记一句话:先把域名托管好,再谈图床。顺序反了,后面每一步都要返工。
评论区