很多个人博客直接把文章图片上传到服务器本地磁盘,随着图片越来越多,备份包体积急速膨胀,而且服务器本身的小水管带宽往往经不起高清图卷的并发请求。
动静分离是解决这个问题的标准方案:服务器只负责输出动态 HTML 页面,所有图片、封面和媒体文件全部托管在专业的对象存储与全球 CDN 上。
Cloudflare R2 提供每月 10GB 免费存储且出站流量完全免费,配合自定义域名解析与 CDN 缓存规则,是个人博客图床的极佳搭档。
这篇笔记记录开通 R2 存储桶、绑定个性域名、生成 S3 凭据并在 Halo 博客中接入 PictureBed 插件的配置过程。
为什么博客需要做动静分离
很多新手刚开始建站,喜欢把图片直接上传到服务器本地,半年后就会遭遇三大灾难:
┌─────────────────────────────────────────────────────────────┐
│ 🔴 传统本地图床的死穴 │
│ - 磁盘撑爆:几百张单反原图塞满小 VPS,博客卡死; │
│ - 备份庞大:每次备份数据库,动辄被十几 GB 图片拖死; │
│ - 带宽瓶颈:小机器 1M~3M 带宽,访客打开带大图的文章要等10秒。│
├─────────────────────────────────────────────────────────────┤
│ 🟢 Cloudflare R2 动静分离架构 │
│ - 宿主机 0 占用:文字与数据库留在服务器,所有图片直奔云端; │
│ - 💰 0 出口流量费:AWS S3 下载流量收天价,而 R2 外网流量 0 元;│
│ - 🎁 10GB 免费空间:个人博客插图几年都用不完; │
│ - ⚡ 全球 CDN 加速:绑定自己的图片二级域名,全球毫秒秒开! │
└─────────────────────────────────────────────────────────────┘
准备工作
- [ ] 一个正常运行的 Halo 博客:能够正常登录
/console管理后台; - [ ] 一个免费 Cloudflare 账号:官网注册即可;
- [ ] 一个托管在 Cloudflare 的域名:准备给图床单独分配一个二级域名(例如
img.0000996.xyz)。
创建 Cloudflare R2 存储桶
💻 【你本地的浏览器】 打开 Cloudflare 仪表盘:
- 在左侧主菜单中点击 「R2 对象存储」;
- 第一次使用会提示开启 R2 服务(完全免费,未超出 10GB 不产生任何费用);
- 点击 「创建存储桶 (Create Bucket)」:
* 存储桶名称:起一个英文名字(例如blog-images);
* 位置:选择 「自动 (Automatic)」 或 「亚太 (APAC)」;
* 点击 「创建存储桶」!
- ✅ 成功标志:页面跳转进入该存储桶的概览页,状态显示正常。
绑定自定义图片域名并开启公开访问
默认创建的存储桶是完全私密的,外界无法直接通过链接看图。我们给它绑上专属域名:
💻 【你本地的浏览器】 在当前存储桶管理页:
- 点击顶部的 「设置 (Settings)」 标签;
- 找到 「公共访问权限 (Public Access)」 区域;
- 找到 「自定义网域 (Custom Domains)」,点击 「连接网域」;
- 输入你想给图床用的二级域名(例如
img.yourdomain.com); - 点击继续,Cloudflare 会全自动在你的 DNS 里补上一条解析记录;
- 点击 「连接网域」 确认。
- ✅ 成功标志:自定义网域列表中显示你的图片域名,状态显示为绿色的
有效 (Active)。
生成 S3 兼容 API 访问凭据
Halo 插件是通过标准的 AWS S3 协议向 R2 推送图片的,需要一对公私密钥。
💻 【你本地的浏览器】:
- 回到 Cloudflare 左侧菜单的 「R2 对象存储」 首页;
- 在右侧上方找到并点击 「管理 R2 API 令牌 (Manage R2 API Tokens)」;
- 点击 「创建 API 令牌 (Create API Token)」:
* 令牌名称:随便填,如halo-pic-token;
* 权限:⚠️ 极其关键!必须勾选「对象读和写 (Object Read & Write)」;
* 应用到特定存储桶:可以选择刚才创建的blog-images,或者所有存储桶;
* TTL 有效期:选择 「永久有效」; - 点击页面最底部的 「创建 API 令牌」。
屏幕上将展示仅出现一次的凭据密匙(如下图):
=====================================================
凭据保护区(请立刻复制保存进便签!)
访问密钥 ID (Access Key ID): xxxx...xxxx
机密访问密钥 (Secret Access Key): yyyy...yyyy
终结点 (Endpoint): https://<账户ID>.r2.cloudflarestorage.com
=====================================================
⚠️ 关键动作:立刻把这三行内容复制并妥善保存在你本地的记事本中!关闭页面后机密密钥将永远无法再次查看。
在 Halo 博客中配置 S3 图床插件
现在把云端图床无缝装进 Halo 的写作后台。
💻 【你本地的浏览器】 登录你的 Halo 博客后台(https://你的博客域名/console):
- 点击左侧菜单 「应用中心」➔「插件」;
- 点击应用市场,搜索
PictureBed(或对象存储插件),点击安装并启用; - 启用后,点击左侧菜单新出现的 「存储策略」(或在插件配置里点击设置):
- 点击 「添加存储策略」,存储类型选择
AWS S3(因为 R2 完美兼容 S3 协议):
* 策略名称:填Cloudflare R2 图床;
* Bucket(存储桶名称):填你在第 1 步起的名字(如blog-images);
* Access Key ID:粘贴刚才保存的「访问密钥 ID」;
* Secret Access Key:粘贴刚才保存的「机密访问密钥」;
* Region(区域):填auto或us-east-1(R2 填auto即可);
* Endpoint(端点地址):⚠️ 防坑核心!填刚才保存的https://<账户ID>.r2.cloudflarestorage.com,末尾绝不要带斜杠/,也不要带存储桶名!
* 自定义访问域名:填入在第 2 步绑定的图片域名(例如https://img.yourdomain.com); - 点击 「保存并设为默认存储策略」!
- ✅ 成功标志:存储策略列表中,R2 图床策略被高亮标记为「默认」。
上传测试与 CDN 加载验证
我们来验证整套“动静分离”链路是否 100% 跑通。
💻 【你本地的浏览器】 在 Halo 后台:
- 点击左侧 「文章」➔「新建文章」;
- 随手从本地电脑拖拽一张图片扔进 Markdown 编辑器窗口;
- 观察编辑器底部进度条:图片上传完毕后,编辑器会自动生成一行 Markdown 图片代码:
markdown
 - 复制这段图片 URL 在浏览器新标签页打开。
- ✅ 大功告成标志:图片瞬间加载呈现,URL 为你的独立图床域名,且浏览器地址栏显示安全小绿锁!
此时再去服务器敲 df -h 查看磁盘占用,你会发现服务器磁盘空间没有增加半个字节,所有图片全在 Cloudflare 全球节点为你免费待命!🚀
配置 Cloudflare 缓存规则优化图片加载
为了让图片加载更快、进一步降低 R2 的读请求计数,可以在 Cloudflare 开启静态长缓存:
- 打开 Cloudflare 控制台,进入你的域名管理页;
- 点击左侧 「缓存」➔「缓存规则 (Cache Rules)」,点击创建规则;
- 匹配条件:主机名等于你的图片域名(例如
img.yourdomain.com); - 缓存资格:设为 「符合缓存条件」;
- 边缘缓存 TTL:设为 「忽略无缓存标头,覆盖 TTL」➔「1 个月」;
- 保存生效!从此访客看图几乎全部直接从离他最近的 CDN 边缘节点秒开!
常见问题与排障
| 异常现场 | 真实根因 | 对应一秒救命招式 |
|---|---|---|
拖入图片提示 403 Forbidden |
API 令牌权限选错了(只给了读权限,没给写权限) | 回到第 3 步,重新创建一个 API 令牌,务必勾选「对象读和写」,把新密钥更新到 Halo 后台。 |
| 上传成功,但文章里的图片全是破损红叉 | 自定义域名未正确回填,或未配置协议头 | 检查 Halo 存储策略中的「自定义访问域名」,必须带上 https://(如 https://img.xxxx.com)。 |
上传提示 S3 Connection Failed |
Endpoint 格式填错(多敲了斜杠或手滑填了 bucket) | 检查 Endpoint 必须是纯净的 https://<账户ID>.r2.cloudflarestorage.com,末尾不能有 /。 |
传稍大一点的照片报 413 Request Too Large |
Nginx 反代限制了外部上传体积 | 在你的服务器 Nginx 反代配置的 server 块中加入 client_max_body_size 100M;,执行 nginx -t && systemctl reload nginx。 |
还没有人在这篇手记留下纸条,来做第一个亮灯的人吧。