很多个人博客直接把文章图片上传到服务器本地磁盘,随着图片越来越多,备份包体积急速膨胀,而且服务器本身的小水管带宽往往经不起高清图卷的并发请求。

动静分离是解决这个问题的标准方案:服务器只负责输出动态 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 仪表盘:

  1. 在左侧主菜单中点击 「R2 对象存储」;
  2. 第一次使用会提示开启 R2 服务(完全免费,未超出 10GB 不产生任何费用);
  3. 点击 「创建存储桶 (Create Bucket)」:
    * 存储桶名称:起一个英文名字(例如 blog-images);
    * 位置:选择 「自动 (Automatic)」 或 「亚太 (APAC)」;
    * 点击 「创建存储桶」!
  • ✅ 成功标志:页面跳转进入该存储桶的概览页,状态显示正常。

绑定自定义图片域名并开启公开访问

默认创建的存储桶是完全私密的,外界无法直接通过链接看图。我们给它绑上专属域名:

💻 【你本地的浏览器】 在当前存储桶管理页:

  1. 点击顶部的 「设置 (Settings)」 标签;
  2. 找到 「公共访问权限 (Public Access)」 区域;
  3. 找到 「自定义网域 (Custom Domains)」,点击 「连接网域」;
  4. 输入你想给图床用的二级域名(例如 img.yourdomain.com);
  5. 点击继续,Cloudflare 会全自动在你的 DNS 里补上一条解析记录;
  6. 点击 「连接网域」 确认。
  • ✅ 成功标志:自定义网域列表中显示你的图片域名,状态显示为绿色的 有效 (Active)。

生成 S3 兼容 API 访问凭据

Halo 插件是通过标准的 AWS S3 协议向 R2 推送图片的,需要一对公私密钥。

💻 【你本地的浏览器】:

  1. 回到 Cloudflare 左侧菜单的 「R2 对象存储」 首页;
  2. 在右侧上方找到并点击 「管理 R2 API 令牌 (Manage R2 API Tokens)」;
  3. 点击 「创建 API 令牌 (Create API Token)」:
    * 令牌名称:随便填,如 halo-pic-token;
    * 权限:⚠️ 极其关键!必须勾选「对象读和写 (Object Read & Write)」;
    * 应用到特定存储桶:可以选择刚才创建的 blog-images,或者所有存储桶;
    * TTL 有效期:选择 「永久有效」;
  4. 点击页面最底部的 「创建 API 令牌」。

屏幕上将展示仅出现一次的凭据密匙(如下图):

=====================================================
凭据保护区(请立刻复制保存进便签!)
访问密钥 ID (Access Key ID):        xxxx...xxxx
机密访问密钥 (Secret Access Key):    yyyy...yyyy
终结点 (Endpoint):                  https://<账户ID>.r2.cloudflarestorage.com
=====================================================

⚠️ 关键动作:立刻把这三行内容复制并妥善保存在你本地的记事本中!关闭页面后机密密钥将永远无法再次查看。


在 Halo 博客中配置 S3 图床插件

现在把云端图床无缝装进 Halo 的写作后台。

💻 【你本地的浏览器】 登录你的 Halo 博客后台(https://你的博客域名/console):

  1. 点击左侧菜单 「应用中心」➔「插件」;
  2. 点击应用市场,搜索 PictureBed(或对象存储插件),点击安装并启用;
  3. 启用后,点击左侧菜单新出现的 「存储策略」(或在插件配置里点击设置):
  4. 点击 「添加存储策略」,存储类型选择 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);
  5. 点击 「保存并设为默认存储策略」!
  • ✅ 成功标志:存储策略列表中,R2 图床策略被高亮标记为「默认」。

上传测试与 CDN 加载验证

我们来验证整套“动静分离”链路是否 100% 跑通。

💻 【你本地的浏览器】 在 Halo 后台:

  1. 点击左侧 「文章」➔「新建文章」;
  2. 随手从本地电脑拖拽一张图片扔进 Markdown 编辑器窗口;
  3. 观察编辑器底部进度条:图片上传完毕后,编辑器会自动生成一行 Markdown 图片代码:
    markdown
    ![测试图片](https://img.yourdomain.com/2026/09/xxxx.jpg)
  4. 复制这段图片 URL 在浏览器新标签页打开。
  • ✅ 大功告成标志:图片瞬间加载呈现,URL 为你的独立图床域名,且浏览器地址栏显示安全小绿锁!

此时再去服务器敲 df -h 查看磁盘占用,你会发现服务器磁盘空间没有增加半个字节,所有图片全在 Cloudflare 全球节点为你免费待命!🚀


配置 Cloudflare 缓存规则优化图片加载

为了让图片加载更快、进一步降低 R2 的读请求计数,可以在 Cloudflare 开启静态长缓存:

  1. 打开 Cloudflare 控制台,进入你的域名管理页;
  2. 点击左侧 「缓存」➔「缓存规则 (Cache Rules)」,点击创建规则;
  3. 匹配条件:主机名等于你的图片域名(例如 img.yourdomain.com);
  4. 缓存资格:设为 「符合缓存条件」;
  5. 边缘缓存 TTL:设为 「忽略无缓存标头,覆盖 TTL」➔「1 个月」;
  6. 保存生效!从此访客看图几乎全部直接从离他最近的 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。