前言
折腾博客怎么能少了图床?用免费的第三方图床总担心跑路,自建又要考虑带宽成本。直到我发现了 Cloudflare R2——每月 10GB 免费存储,无流量费用,配合 Workers 还能实现防盗链。
这篇文章记录我搭建 R2 图床的全过程,包括防盗链配置,确保只有自己的网站能用。
为什么选择 Cloudflare R2
优势:
- ✅ 免费额度大方:每月 10GB 存储 + 1000万次读取
- ✅ 零流量费用:不像 AWS S3 那样按流量计费
- ✅ 速度快:Cloudflare 的 CDN 加速
- ✅ 可控性强:通过 Workers 实现各种自定义功能
免费额度:
- 存储:10 GB/月
- A 类操作(写入):100 万次/月
- B 类操作(读取):1000 万次/月
对于个人博客来说,这个额度完全够用了。
准备工作
- Cloudflare 账号(免费)
- 一个域名(我用的是 example.com)
- 域名已托管到 Cloudflare
第一步:创建 R2 存储桶
登录 Cloudflare Dashboard
进入 R2 页面
- 左侧菜单找到 "R2"
- 如果是第一次使用,点击 "Purchase R2 Plan"(选择免费计划)
创建存储桶
- 点击 "Create bucket"
- Bucket name:
blog-images
(你的存储桶名称,可自定义) - Location:选择 "Automatic"(自动选择最近的位置)
- 点击 "Create bucket"
上传测试图片
- 进入刚创建的存储桶
- 点击 "Upload",上传一张测试图片(比如
test.jpg
)
第二步:创建 Worker 处理请求
进入 Workers & Pages
- 左侧菜单点击 "Workers & Pages"
- 点击 "Create application"
- 选择 "Create Worker"
命名 Worker
- 输入名称:
r2-image-handler
(你的 Worker 名称,可自定义) - 点击 "Deploy"
- 输入名称:
编辑 Worker 代码
- 部署后,点击 "Edit code"
- 删除默认代码,粘贴以下代码:
export default {
async fetch(request, env) {
const url = new URL(request.url);
const referer = request.headers.get('Referer');
// 允许访问的域名列表
const allowedDomains = [
'example.com',
'www.example.com'
];
// 检查 Referer 是否在允许列表中
let isAllowed = false;
if (referer) {
// 检查 Referer 是否包含允许的域名
for (const domain of allowedDomains) {
if (referer.includes(domain)) {
isAllowed = true;
break;
}
}
}
// 如果不允许访问(直接访问或其他网站引用)
if (!isAllowed) {
// 返回防盗链提示图
const warningImage = await env.MY_BUCKET.get('hotlink-warning.jpg');
if (warningImage) {
const headers = new Headers();
headers.set('Content-Type', 'image/jpeg'); // 如果是 PNG 改为 image/png
headers.set('Cache-Control', 'public, max-age=3600'); // 缓存1小时
headers.set('X-Robots-Tag', 'noindex');
return new Response(warningImage.body, {
status: 200,
headers
});
}
// 如果提示图不存在,返回文字提示
return new Response('Hotlinking is not allowed. Please visit https://example.com', {
status: 403,
headers: {
'Content-Type': 'text/plain',
'X-Robots-Tag': 'noindex'
}
});
}
// 从 R2 获取文件
const objectKey = url.pathname.slice(1); // 去掉开头的 /
// 如果路径为空,返回提示
if (!objectKey) {
return new Response('R2 Image CDN - Please specify image path', {
status: 400,
headers: { 'Content-Type': 'text/plain' }
});
}
// 从 R2 获取对象
const object = await env.MY_BUCKET.get(objectKey);
if (object === null) {
return new Response('Image Not Found', {
status: 404,
headers: { 'Content-Type': 'text/plain' }
});
}
// 设置响应头
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set('etag', object.httpEtag);
// 设置缓存(缓存一年)
headers.set('Cache-Control', 'public, max-age=31536000, immutable');
// 设置 CORS(允许跨域)
headers.set('Access-Control-Allow-Origin', '*');
// 返回图片
return new Response(object.body, {
headers
});
}
};
保存并部署
- 点击右上角 "Save and deploy"
第三步:绑定 R2 存储桶
进入 Worker 设置
- 回到 Worker 列表,点击刚创建的
r2-image-handler
- 点击 "Settings" 标签
- 回到 Worker 列表,点击刚创建的
添加 R2 绑定
- 找到 "Variables and Secrets"
- 向下滚动找到 "R2 Bucket Bindings"
- 点击 "Add binding"
- Variable name:
MY_BUCKET
(⚠️ 必须和代码中的一致) - R2 bucket:选择
blog-images
(你的存储桶名称) - 点击 "Save"
第四步:配置自定义域名
添加自定义域名
- 在 Worker 页面,点击 "Settings"
- 找到 "Domains & Routes"
- 点击 "Add" 选择 "Custom Domain"
- 输入子域名:
img.example.com
(改成你自己的域名) - 点击 "Add Custom Domain"
等待 DNS 生效
- Cloudflare 会自动添加 DNS 记录
- 通常几分钟内就能生效
第五步:测试图床
1. 上传图片到 R2
在 R2 存储桶中上传几张图片,比如:
test.jpg
avatar.png
cover.jpg
2. 测试访问
直接访问:
https://img.example.com/test.jpg
应该能看到图片。
3. 准备防盗链提示图
为了让盗链者看到友好的提示而不是空白,需要准备一张提示图片:
制作提示图
- 推荐尺寸:1200x630 或 800x600
- 格式:JPG 或 PNG
- 文件大小:控制在 100KB 以内
内容示例:
🚫 禁止盗链 🚫 此图片仅供个人博客使用 请访问:example.com
上传到 R2
- 进入 R2 控制台,选择
blog-images
存储桶 - 上传你的提示图,文件名必须是:
hotlink-warning.jpg
- (如果是 PNG 格式,记得修改 Worker 代码中的
Content-Type
为image/png
)
- 进入 R2 控制台,选择
4. 测试防盗链
在 PowerShell 中测试:
# 测试从你的网站访问(应该显示原图)
curl -I -H "Referer: https://example.com" https://img.example.com/test.jpg
# 测试直接访问(应该显示防盗链提示图)
curl -I https://img.example.com/test.jpg
# 测试从其他网站访问(应该显示防盗链提示图)
curl -I -H "Referer: https://other-site.com" https://img.example.com/test.jpg
测试结果:
- 从 example.com 访问 → 显示原图 ✅
- 直接访问 → 显示
hotlink-warning.jpg
✅ - 从其他网站访问 → 显示
hotlink-warning.jpg
✅
在博客中使用
Markdown 语法

HTML 语法
<img src="https://img.example.com/your-image.jpg" alt="图片描述">
Typecho 中使用
直接在编辑器中插入图片时,将链接改为 R2 的地址即可。
配置 PicList 实现自动上传
完成 R2 和 Worker 配置后,最重要的就是配置上传工具。我选择了 PicList(PicGo 的增强版),可以实现截图后自动上传到 R2。
第一步:获取 R2 的 API Token
进入 Cloudflare R2 页面
- 访问:https://dash.cloudflare.com
- 左侧菜单点击 "R2"
创建 API Token
- 点击右上角 "Manage R2 API Tokens"
- 点击 "Create API Token"
- Token name:
piclist-upload
(随便起名) - Permissions:选择 "Object Read & Write"
- 如果想限制只能访问特定存储桶,在 "Specify bucket(s)" 中选择
blog-images
- 点击 "Create API Token"
保存凭证信息
创建成功后会显示以下信息,务必保存(只显示一次):
Access Key ID: xxxxxxxxxxxxxxxxxxxx Secret Access Key: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy Endpoint: https://你的账号ID.r2.cloudflarestorage.com
第二步:下载并安装 PicList
下载 PicList
- 官网:https://piclist.cn/
- GitHub:https://github.com/Kuingsmile/PicList/releases
- 下载 Windows 版本(.exe 安装包)
安装 PicList
- 双击安装包,按提示安装即可
第三步:配置 PicList 的 S3 图床
PicList 原生支持 S3 协议,R2 完全兼容 S3 API,所以可以直接使用 S3 配置。
打开 PicList
- 启动后会在系统托盘显示图标
进入图床设置
- 右键托盘图标 → "打开详细窗口"
- 左侧菜单点击 "图床设置"
- 选择 "Amazon S3"
填写配置信息
根据你刚才保存的 API Token 信息填写:
配置项 填写内容 说明 应用密钥 ID 你的 Access Key ID 就是刚才保存的 Access Key 应用密钥 你的 Secret Access Key 就是刚才保存的 Secret Key 桶名 blog-images
你的 R2 存储桶名称 文件路径 {year}/{month}/
按年月分类存储(可选) 自定义节点 你的账号ID.r2.cloudflarestorage.com
⚠️ 去掉 https:// 自定义域名 https://img.example.com
你的自定义域名 地区 auto
自动选择 设置为默认图床
- 配置完成后,点击 "确定"
- 点击 "设为默认图床"
第四步:测试上传
截图上传测试
- 按快捷键
Ctrl + Shift + P
(默认) - 框选截图区域
- 截图后会自动上传
- 上传成功后,图片链接会自动复制到剪贴板
- 按快捷键
文件上传测试
- 右键托盘图标 → "打开详细窗口"
- 点击 "上传区"
- 将图片拖拽到窗口
- 或点击 "选择图片" 上传
验证结果
- 上传成功后会显示图片链接
- 访问链接确认图片能正常显示
- 链接格式应该是:
https://img.example.com/2025/10/xxx.png
配置说明
关于文件路径:
{year}/{month}/
会按照以下格式存储:
blog-images/
├── 2025/
│ ├── 10/
│ │ ├── image1.png
│ │ └── image2.jpg
│ └── 11/
└── 2026/
你也可以自定义为:
{year}-{month}-{day}/
- 按日期分类blog/
- 统一放在 blog 文件夹- 留空 - 直接存在根目录
关于自定义域名:
- ✅ 填写:
https://img.example.com
- 上传后返回你的自定义域名链接 - ❌ 不填:上传后返回 R2 原始链接(不推荐)
PicList 常用功能
快捷键设置
- 设置 → 快捷键设置
- 可以自定义截图、上传等快捷键
上传历史
- 相册 → 查看所有上传过的图片
- 可以复制链接、删除图片等
压缩图片
- 设置 → PicGo 设置 → 图片压缩
- 开启后上传前自动压缩,节省空间
水印功能
- 插件设置 → 安装水印插件
- 可以给图片自动加水印
常见问题
Q1: 上传失败,提示 "Network Error"
A: 检查以下几点:
- Access Key 和 Secret Key 是否正确
- 自定义节点是否去掉了
https://
- 桶名是否正确(
blog-images
) - 网络是否正常
Q2: 上传成功但返回的链接无法访问
A:
- 检查 "自定义域名" 是否填写了
https://img.example.com
- 检查域名是否已经绑定到 Worker
- 检查 Worker 是否正确绑定了 R2 存储桶
Q3: 图片上传后找不到
A:
- 去 Cloudflare R2 控制台查看存储桶
- 根据你设置的 "文件路径" 查找对应目录
- 比如设置了
{year}/{month}/
,就去对应的年月文件夹找
Q4: 可以批量上传吗?
A: 可以!
- 方法1:在 PicList 上传区,一次选择多张图片
- 方法2:直接把多张图片拖拽到上传区
- 方法3:使用 PicList 的相册批量管理功能
Q5: PicList 相册不显示图片怎么办?
A: 这是正常的。S3 协议不支持相册功能,但可以:
- 查看 PicList 的"上传历史"(本地记录)
- 去 Cloudflare R2 控制台查看所有文件
- 上传成功后链接会自动复制,可以保存在文档里
Q6: 防盗链提示图显示不出来?
A: 检查以下几点:
- 提示图文件名是否为
hotlink-warning.jpg
(必须完全一致) - 提示图是否已上传到 R2 存储桶根目录
- 如果用 PNG 格式,代码中的
Content-Type
要改为image/png
成本分析
个人博客使用完全免费:
- 存储:10GB 免费额度
- 读取:1000万次/月免费
- 流量:完全免费(这是最大优势)
对比其他图床方案:
- 七牛云:流量费 ¥0.29/GB
- 又拍云:需要备案 + 流量费
- 阿里云 OSS:存储费 + 流量费
- Cloudflare R2:0 元!
关于防盗链的说明
配置完成后的效果:
✅ 从你的网站访问
在 example.com 的博客文章中,图片正常显示:
<img src="https://img.example.com/2025/10/screenshot.png">
→ 显示原图
❌ 直接访问或其他网站盗链
在浏览器地址栏直接打开,或者其他网站引用:
https://img.example.com/2025/10/screenshot.png
→ 显示防盗链提示图 hotlink-warning.jpg
这样做的好处:
- 🔒 保护你的带宽,防止被薅羊毛
- 🎯 盗链者看到的是提示图,可以为你的博客引流
- 💰 节省 R2 的读取次数配额
实际使用体验
配置完成后,我的使用流程变成了:
- 写博客需要图片时,按
Ctrl + Shift + P
截图 - 图片自动上传到 R2,链接自动复制到剪贴板
- 在 Markdown 编辑器中
Ctrl + V
粘贴 - 完成!
体验总结:
- ✅ 速度快:Cloudflare CDN 加速,国内访问也很快
- ✅ 成本低:完全免费,无需担心流量费用
- ✅ 操作简单:PicList 配置好后,截图即上传
- ✅ 防盗链:只有自己网站能用,别人盗链显示提示图
总结
用 Cloudflare R2 搭建图床的优势:
- ✅ 完全免费(个人博客够用)
- ✅ 速度快(Cloudflare CDN)
- ✅ 可控性强(自己掌握数据)
- ✅ 防盗链(保护带宽)
- ✅ 稳定可靠(Cloudflare 的基础设施)
从此告别第三方图床跑路的担忧!
2025.10.21 · 济南
终于有了自己的图床,从此告别第三方跑路的担忧!
P.S. 如果你也在用 R2 + PicList,欢迎评论区交流经验!