> ## Content Index
> Fetch the complete content index at: https://tbbbk.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Cloudflare R2 图床搭建教程：免费 + 防盗链 + PicList 自动上传
- URL: https://tbbbk.com/cloudflare-r2-imgbed/
- Published: 2025-10-21T21:19:00.000Z
- Updated: 2026-01-30T05:45:49.000Z
- Description: 博客中使用 Cloudflare R2 搭建免费图床，配合 Workers 防盗链 与 PicList 自动上传，实现零流量费、国内加速与带宽保护，提升页面加载速度与 SEO 体验。
- Author: TuBaiBai
- Tags: 建站笔记, #Import 2026-07-20 07:13

## 前言

折腾博客怎么能少了图床？用免费的第三方图床总担心跑路，自建又要考虑带宽成本。直到我发现了 **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 存储桶

1. **登录 Cloudflare Dashboard**

  - 访问：<https://dash.cloudflare.com>
2. **进入 R2 页面**

  - 左侧菜单找到 "R2"
  - 如果是第一次使用，点击 "Purchase R2 Plan"（选择免费计划）
3. **创建存储桶**

  - 点击 "Create bucket"
  - Bucket name：`blog-images`（你的存储桶名称，可自定义）
  - Location：选择 "Automatic"（自动选择最近的位置）
  - 点击 "Create bucket"
4. **上传测试图片**

  - 进入刚创建的存储桶
  - 点击 "Upload"，上传一张测试图片（比如 `test.jpg`）

## 第二步：创建 Worker 处理请求

1. **进入 Workers & Pages**

  - 左侧菜单点击 "Workers & Pages"
  - 点击 "Create application"
  - 选择 "Create Worker"
2. **命名 Worker**

  - 输入名称：`r2-image-handler`（你的 Worker 名称，可自定义）
  - 点击 "Deploy"
3. **编辑 Worker 代码**

  - 部署后，点击 "Edit code"
  - 删除默认代码，粘贴以下代码：

```javascript
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
});

```

1. **保存并部署**

  - 点击右上角 "Save and deploy"

## 第三步：绑定 R2 存储桶

1. **进入 Worker 设置**

  - 回到 Worker 列表，点击刚创建的 `r2-image-handler`
  - 点击 "Settings" 标签
2. **添加 R2 绑定**

  - 找到 "Variables and Secrets"
  - 向下滚动找到 "R2 Bucket Bindings"
  - 点击 "Add binding"
  - Variable name：`MY_BUCKET`（⚠️ 必须和代码中的一致）
  - R2 bucket：选择 `blog-images`（你的存储桶名称）
  - 点击 "Save"

## 第四步：配置自定义域名

1. **添加自定义域名**

  - 在 Worker 页面，点击 "Settings"
  - 找到 "Domains & Routes"
  - 点击 "Add" 选择 "Custom Domain"
  - 输入子域名：`img.example.com`（改成你自己的域名）
  - 点击 "Add Custom Domain"
2. **等待 DNS 生效**

  - Cloudflare 会自动添加 DNS 记录
  - 通常几分钟内就能生效

## 第五步：测试图床

### 1\. 上传图片到 R2

在 R2 存储桶中上传几张图片，比如：

- `test.jpg`
- `avatar.png`
- `cover.jpg`

### 2\. 测试访问

直接访问：

```
https://img.example.com/test.jpg
```

应该能看到图片。

### 3\. 准备防盗链提示图

为了让盗链者看到友好的提示而不是空白，需要准备一张提示图片：

1. **制作提示图**

  - 推荐尺寸：**1200x630** 或 **800x600**
  - 格式：JPG 或 PNG
  - 文件大小：控制在 100KB 以内
  - 内容示例：  
  ```  
  🚫 禁止盗链 🚫  
  此图片仅供个人博客使用  
  请访问：example.com  
  ```
2. **上传到 R2**

  - 进入 R2 控制台，选择 `blog-images` 存储桶
  - 上传你的提示图，**文件名必须是**：`hotlink-warning.jpg`
  - （如果是 PNG 格式，记得修改 Worker 代码中的 `Content-Type` 为 `image/png`）

### 4\. 测试防盗链

在 PowerShell 中测试：

```powershell
# 测试从你的网站访问（应该显示原图）
curl -I -H "Referer: https://example.com" https://img.example.com/test.jpg
测试直接访问（应该显示防盗链提示图）
curl -I https://img.example.com/test.jpg
测试从其他网站访问（应该显示防盗链提示图）

```

**测试结果：**

- 从 example.com 访问 → 显示原图 ✅
- 直接访问 → 显示 `hotlink-warning.jpg` ✅
- 从其他网站访问 → 显示 `hotlink-warning.jpg` ✅

## 在博客中使用

### Markdown 语法

### HTML 语法

```html
<img src="https://img.example.com/your-image.jpg" alt="图片描述">
```

### Typecho 中使用

直接在编辑器中插入图片时，将链接改为 R2 的地址即可。

## 配置 PicList 实现自动上传

完成 R2 和 Worker 配置后，最重要的就是配置上传工具。我选择了 **PicList**（PicGo 的增强版），可以实现截图后自动上传到 R2。

### 第一步：获取 R2 的 API Token

1. **进入 Cloudflare R2 页面**

  - 访问：<https://dash.cloudflare.com>
  - 左侧菜单点击 "R2"
2. **创建 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"
3. **保存凭证信息**  
创建成功后会显示以下信息，**务必保存**（只显示一次）：  
```  
Access Key ID: xxxxxxxxxxxxxxxxxxxx  
Secret Access Key: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy  
Endpoint: https://你的账号ID.r2.cloudflarestorage.com  
```

### 第二步：下载并安装 PicList

1. **下载 PicList**

  - 官网：<https://piclist.cn/>
  - GitHub：<https://github.com/Kuingsmile/PicList/releases>
  - 下载 Windows 版本（.exe 安装包）
2. **安装 PicList**

  - 双击安装包，按提示安装即可

### 第三步：配置 PicList 的 S3 图床

PicList 原生支持 S3 协议，R2 完全兼容 S3 API，所以可以直接使用 S3 配置。

1. **打开 PicList**

  - 启动后会在系统托盘显示图标
2. **进入图床设置**

  - 右键托盘图标 → "打开详细窗口"
  - 左侧菜单点击 "图床设置"
  - 选择 "Amazon S3"
3. **填写配置信息**  
根据你刚才保存的 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                            | 自动选择               |
4. **设置为默认图床**

  - 配置完成后，点击 "确定"
  - 点击 "设为默认图床"

### 第四步：测试上传

1. **截图上传测试**

  - 按快捷键 `Ctrl + Shift + P`（默认）
  - 框选截图区域
  - 截图后会自动上传
  - 上传成功后，图片链接会自动复制到剪贴板
2. **文件上传测试**

  - 右键托盘图标 → "打开详细窗口"
  - 点击 "上传区"
  - 将图片拖拽到窗口
  - 或点击 "选择图片" 上传
3. **验证结果**

  - 上传成功后会显示图片链接
  - 访问链接确认图片能正常显示
  - 链接格式应该是：`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 常用功能

1. **快捷键设置**

  - 设置 → 快捷键设置
  - 可以自定义截图、上传等快捷键
2. **上传历史**

  - 相册 → 查看所有上传过的图片
  - 可以复制链接、删除图片等
3. **压缩图片**

  - 设置 → PicGo 设置 → 图片压缩
  - 开启后上传前自动压缩，节省空间
4. **水印功能**

  - 插件设置 → 安装水印插件
  - 可以给图片自动加水印

### 常见问题

**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 的博客文章中，图片正常显示：

```html
<img src="https://img.example.com/2025/10/screenshot.png">
```

→ 显示原图

### ❌ 直接访问或其他网站盗链

在浏览器地址栏直接打开，或者其他网站引用：

```
https://img.example.com/2025/10/screenshot.png
```

→ 显示防盗链提示图 `hotlink-warning.jpg`

**这样做的好处：**

- 🔒 保护你的带宽，防止被薅羊毛
- 🎯 盗链者看到的是提示图，可以为你的博客引流
- 💰 节省 R2 的读取次数配额

## 实际使用体验

配置完成后，我的使用流程变成了：

1. 写博客需要图片时，按 `Ctrl + Shift + P` 截图
2. 图片自动上传到 R2，链接自动复制到剪贴板
3. 在 Markdown 编辑器中 `Ctrl + V` 粘贴
4. 完成！

**体验总结：**

- ✅ 速度快：Cloudflare CDN 加速，国内访问也很快
- ✅ 成本低：完全免费，无需担心流量费用
- ✅ 操作简单：PicList 配置好后，截图即上传
- ✅ 防盗链：只有自己网站能用，别人盗链显示提示图

## 总结

用 Cloudflare R2 搭建图床的优势：

- ✅ **完全免费**（个人博客够用）
- ✅ **速度快**（Cloudflare CDN）
- ✅ **可控性强**（自己掌握数据）
- ✅ **防盗链**（保护带宽）
- ✅ **稳定可靠**（Cloudflare 的基础设施）

从此告别第三方图床跑路的担忧！

---

**2025.10.21 · 济南**   
终于有了自己的图床，从此告别第三方跑路的担忧！

*P.S. 如果你也在用 R2 + PicList，欢迎评论区交流经验！*