PicGo + Cloudflare R2 配置与排障手册
本文记录一套已经实际使用的 PicGo + Cloudflare R2 配置,并提供从快速检查到深入定位的排障流程。
配置概览
上传链路:
PicGo → picgo-plugin-s3 → 本地 HTTP 代理(可选)→ Cloudflare R2访问链路:
Markdown 图片 URL → cdn.yunshux.com → Cloudflare R2两条链路相互独立:CDN 访问很快,不代表 R2 上传一定快;R2 Endpoint 可连接,也不代表 CDN 域名配置正确。
当前示例使用以下配置:
Bucket:yunshu
Region:auto
Endpoint:https://<ACCOUNT_ID>.r2.cloudflarestorage.com
上传路径:images/{year}/{month}/{fileName}-{year}{month}{day}-{md5:0,8}.{extName}
HTTP 代理:http://127.0.0.1:7897
CDN:https://cdn.yunshux.com
WebP:quality 80,method 6其中 <ACCOUNT_ID>、Access Key ID 和 Secret Access Key 必须替换为自己的值;代理端口应以 macOS 当前系统代理为准。
前置准备
创建 R2 Bucket 和访问凭据
- 在 Cloudflare 控制台进入 R2 Object Storage,创建名为
yunshu的 bucket。 - 在 R2 的 API Token 管理页面创建凭据。
- 将权限范围限制到
yunshu,仅授予上传所需的最小权限。PicGo S3 上传通常使用该 bucket 的 Object Read & Write。 - 保存生成的 Access Key ID 和 Secret Access Key。Secret Access Key 通常只展示一次。
密钥安全
不要把 Access Key ID、Secret Access Key 或完整 PicGo 配置写进本文、截图、聊天记录和 Git。排障命令只需要 Account ID,不需要真实密钥。
绑定自定义域名
在 Cloudflare 控制台进入:
R2 → yunshu → Settings → Custom Domains绑定 cdn.yunshux.com,等待状态变为可用,再访问一个已上传对象验证域名。自定义域名负责公开读取;R2 S3 Endpoint 负责 PicGo 上传。
安装 PicGo 插件
在 PicGo 的插件设置中安装:
picgo-plugin-s3如果需要上传前转为 WebP,再安装:
picgo-plugin-compress-next安装完成后重启 PicGo,确认图床设置中出现 Amazon S3,并将它设为默认图床。
PicGo 配置
Amazon S3 基础配置
在 PicGo → 图床设置 → Amazon S3 中填写:
应用密钥 ID:
自己的 R2 Access Key ID
应用密钥:
自己的 R2 Secret Access Key
桶名:
yunshu
上传文件路径:
images/{year}/{month}/{fileName}-{year}{month}{day}-{md5:0,8}.{extName}
地区:
auto
自定义节点:
https://<ACCOUNT_ID>.r2.cloudflarestorage.com
代理:
http://127.0.0.1:7897代理不是固定必填项。如果当前网络直连 R2 上传稳定,可以留空;如果直连很慢而 HTTP 代理链路正常,再填写系统当前代理。
ACL 和寻址方式
Cloudflare R2 不支持通过对象级公开 ACL 实现公开访问。插件存在 ACL 字段时:
- 优先留空或关闭该选项,使插件不发送 ACL。
- 如果 UI 强制要求选择,使用
private。 - 不要使用
public-read。 - 公开访问统一通过 R2 自定义域名或其前置 CDN。
pathStyleAccess 不要机械开启
不同 picgo-plugin-s3 版本和 AWS SDK 的字段语义可能不同。先保持插件默认值;只有出现 bucket host、签名或 Endpoint 相关错误,并确认当前插件把 pathStyleAccess: true 定义为路径式访问时,再开启并复测。不要把它当作所有 R2 配置的固定要求。
输出 URL 和 WebP
根据当前插件界面选择一种配置方式:
- UI 字段是“图片访问域名”时,填写
https://cdn.yunshux.com。 - 使用插件原始配置
outputURLPattern时,填写https://cdn.yunshux.com/{path}。
不要在基础域名后重复拼接上传路径,否则可能生成两段相同目录。
启用 picgo-plugin-compress-next 后配置:
格式:WebP
quality:80
method:6首次配置后上传一张不含敏感信息的测试图片,并确认:
- PicGo 日志包含
Uploading... Current uploader is [aws-s3]。 - 最终出现
PicGo SUCCESS。 - 返回 URL 以
https://cdn.yunshux.com/开头。 - 浏览器能够打开该 URL。
30 秒快速排障
按以下顺序检查,不需要先运行 Wrangler。
查看 macOS 当前系统代理:
bashscutil --proxy优先查看已启用的
HTTPEnable、HTTPProxy和HTTPPort。没有启用 HTTP 代理时,再查看已启用的HTTPSEnable、HTTPSProxy和HTTPSPort。检查代理端口。以下为当前示例,端口变化后必须替换:
bashnc -vz -w 3 127.0.0.1 7897出现
succeeded表示端口可连接;出现Connection refused或超时,通常是代理软件未启动、系统代理未开启或端口已经变化。将 PicGo 代理更新为当前 HTTP 代理,例如:
texthttp://127.0.0.1:7897再上传一张测试图片,对比日志中
Uploading...到PicGo SUCCESS的耗时。
代理协议
picgo-plugin-s3 的代理字段需要 HTTP 代理 URL 时,不能直接填写 socks5://...。如果系统只有 SOCKS 代理,请在代理软件中开启 HTTP/mixed 端口,再填写 http://主机:端口。
如果仍失败,先确认 Amazon S3 是默认图床。日志若显示 [picgo-cloud],说明当前上传器选错,与 R2 网络无关。
一键排障脚本
脚本仅适用于 macOS,长期保存到 ~/.local/bin/picgo-r2-check。它不会读取或输出任何 R2 密钥,也不会修改 PicGo 配置。
安装脚本
先把脚本中的 YOUR_ACCOUNT_ID 替换为自己的 Cloudflare Account ID,再执行整段命令:
mkdir -p ~/.local/bin
cat > ~/.local/bin/picgo-r2-check <<'EOF'
#!/usr/bin/env bash
set -u
for cmd in scutil nc curl; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "❌ 缺少依赖:$cmd"
exit 1
fi
done
ACCOUNT_ID="YOUR_ACCOUNT_ID"
BUCKET="yunshu"
CDN="https://cdn.yunshux.com"
UPLOAD_PATH='images/{year}/{month}/{fileName}-{year}{month}{day}-{md5:0,8}.{extName}'
if [[ -z "$ACCOUNT_ID" || "$ACCOUNT_ID" == "YOUR_ACCOUNT_ID" ]]; then
echo "❌ 请先把脚本中的 YOUR_ACCOUNT_ID 替换为自己的 Cloudflare Account ID"
exit 1
fi
R2="https://${ACCOUNT_ID}.r2.cloudflarestorage.com"
PROXY_DATA="$(scutil --proxy)"
proxy_value() {
printf '%s\n' "$PROXY_DATA" | awk -v key="$1" '$1 == key { print $3; exit }'
}
HTTP_ENABLE="$(proxy_value HTTPEnable)"
HTTP_HOST="$(proxy_value HTTPProxy)"
HTTP_PORT="$(proxy_value HTTPPort)"
HTTPS_ENABLE="$(proxy_value HTTPSEnable)"
HTTPS_HOST="$(proxy_value HTTPSProxy)"
HTTPS_PORT="$(proxy_value HTTPSPort)"
SOCKS_ENABLE="$(proxy_value SOCKSEnable)"
SOCKS_HOST="$(proxy_value SOCKSProxy)"
SOCKS_PORT="$(proxy_value SOCKSPort)"
PROXY=""
PROXY_HOST=""
PROXY_PORT=""
PROXY_SOURCE=""
if [[ "$HTTP_ENABLE" == "1" && -n "$HTTP_HOST" && -n "$HTTP_PORT" ]]; then
PROXY_HOST="$HTTP_HOST"
PROXY_PORT="$HTTP_PORT"
PROXY_SOURCE="HTTP"
elif [[ "$HTTPS_ENABLE" == "1" && -n "$HTTPS_HOST" && -n "$HTTPS_PORT" ]]; then
PROXY_HOST="$HTTPS_HOST"
PROXY_PORT="$HTTPS_PORT"
PROXY_SOURCE="HTTPS(按 HTTP CONNECT 使用)"
fi
if [[ -n "$PROXY_HOST" && -n "$PROXY_PORT" ]]; then
PROXY="http://${PROXY_HOST}:${PROXY_PORT}"
fi
check_url() {
local label="$1"
shift
echo
echo "$label"
curl --connect-timeout 5 --max-time 15 \
-o /dev/null -sS \
-w 'HTTP: %{http_code}\nDNS: %{time_namelookup}s\nTCP: %{time_connect}s\nTLS: %{time_appconnect}s\nTTFB: %{time_starttransfer}s\nTOTAL: %{time_total}s\n' \
"$@"
local status=$?
if [[ $status -ne 0 ]]; then
echo "curl exit: $status"
fi
}
echo
echo "======================================"
echo " PicGo + Cloudflare R2 一键排障"
echo "======================================"
echo
echo "[1/5] 检查 macOS 系统代理"
if [[ -n "$PROXY" ]]; then
echo "代理来源:$PROXY_SOURCE"
echo "HTTP 代理:$PROXY"
else
echo "⚠️ 未检测到已启用的 HTTP/HTTPS 代理"
if [[ "$SOCKS_ENABLE" == "1" && -n "$SOCKS_HOST" && -n "$SOCKS_PORT" ]]; then
echo "检测到 SOCKS:socks5://${SOCKS_HOST}:${SOCKS_PORT}"
echo "PicGo S3 代理字段不能直接照填 SOCKS,请启用 HTTP/mixed 端口。"
fi
fi
echo
echo "[2/5] 检查代理端口"
if [[ -n "$PROXY" ]]; then
if nc -z -w 3 "$PROXY_HOST" "$PROXY_PORT" 2>/dev/null; then
echo "✅ 代理端口可连接:${PROXY_HOST}:${PROXY_PORT}"
else
echo "❌ 代理端口无法连接:${PROXY_HOST}:${PROXY_PORT}"
fi
else
echo "跳过:没有可用的 HTTP/HTTPS 代理"
fi
check_url "[3/5] 测试 R2 直连" "$R2"
if [[ -n "$PROXY" ]]; then
check_url "[4/5] 测试 R2 代理链路" -x "$PROXY" "$R2"
else
echo
echo "[4/5] 测试 R2 代理链路"
echo "跳过:没有可用的 HTTP/HTTPS 代理"
fi
check_url "[5/5] 测试 CDN" "$CDN"
echo
echo "======================================"
echo " PicGo 推荐配置"
echo "======================================"
echo "Bucket : $BUCKET"
echo "Region : auto"
echo "Endpoint : $R2"
echo "Path : $UPLOAD_PATH"
echo "Proxy : ${PROXY:-未检测到,请按当前网络决定是否留空}"
echo "CDN : $CDN"
echo "ACL : 不发送;若必须选择则使用 private"
echo
echo "说明:未签名请求的 HTTP 状态不验证 R2 凭据或实际上传权限。"
EOF
chmod +x ~/.local/bin/picgo-r2-check
~/.local/bin/picgo-r2-check日后运行
直接执行:
~/.local/bin/picgo-r2-check如果希望在任意目录输入 picgo-r2-check,把目录加入 PATH:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc脚本每次从 scutil --proxy 读取当前代理:优先使用已启用的 HTTP 代理,必要时回退到已启用的 HTTPS 代理,因此代理软件切换端口后无需修改脚本。
深入排障
正确理解脚本结果
- R2 直连慢、代理快:PicGo 中填写脚本输出的 HTTP 代理。
- R2 直连和代理都失败:检查 DNS、系统时间、代理规则、TLS 拦截和 Cloudflare 服务状态。
- R2 链路正常但 PicGo 上传失败:检查凭据、bucket、ACL、Endpoint、上传路径和默认上传器。
- 上传成功但 CDN 无法访问:检查自定义域名绑定、对象路径和 DNS;不要继续调整上传代理。
未签名请求的边界
curl 未携带 AWS Signature。访问 R2 Endpoint 返回 400 只能说明请求已经收到 HTTP 响应、链路基本可达,不能验证 Access Key、Secret Access Key、bucket 权限或 PicGo 实际上传。其他 HTTP 状态也应结合响应时间和 PicGo 签名请求判断。
常见配置错误
代理软件未启动或端口变化
重新运行 scutil --proxy 和一键脚本,以脚本输出为准,不要猜测 7890、7897 或 1080。系统只启用 SOCKS 时,在代理软件中开启 HTTP/mixed 端口。
凭据或权限错误
重新创建仅限 yunshu 的 R2 API Token,更新 PicGo 中的 Access Key ID 和 Secret Access Key。不要用普通 Cloudflare API Token 文本代替 S3 凭据。
ACL 错误
删除 public-read。优先让插件不发送 ACL;插件必须选择时使用 private,公开访问交给 cdn.yunshux.com。
上传器错误
日志必须显示:
Uploading... Current uploader is [aws-s3]如果显示 [picgo-cloud],回到 Amazon S3 设置并设为默认图床。
可选:Local Uploads
R2 Local Uploads 在 2026 年仍属于 open beta。它可能改善部分跨区域上传链路,但不是 PicGo 正确上传的前提,也不应作为 30 秒主流程或脚本的依赖。
启用:
npx wrangler r2 bucket local-uploads enable yunshu停用:
npx wrangler r2 bucket local-uploads disable yunshu状态检查请使用 Cloudflare 控制台:
R2 → yunshu → Settings → Local Uploads不要使用官方文档未列出的 local-uploads get 子命令。
可选:Wrangler 登录与网络细查
只有管理 Local Uploads 等 R2 功能时才需要 Wrangler 登录:
npx wrangler login
npx wrangler whoami授权异常时可以重新登录:
npx wrangler logout
npx wrangler login
npx wrangler whoami如果一键脚本仍无法定位,再按需检查:
dig "<ACCOUNT_ID>.r2.cloudflarestorage.com"
traceroute -n "<ACCOUNT_ID>.r2.cloudflarestorage.com"
env | grep -i proxy终端中的 HTTP_PROXY、HTTPS_PROXY 和 ALL_PROXY 不一定会被 PicGo GUI 继承,所以 PicGo 需要代理时仍应在插件配置中显式填写。
重装恢复
按以下清单恢复,密钥从安全的密码管理器中读取:
- 安装 PicGo。
- 安装
picgo-plugin-s3。 - 如需 WebP,安装
picgo-plugin-compress-next。 - 配置 Bucket
yunshu、Regionauto和 R2 Endpoint。 - 填入 R2 Access Key ID 与 Secret Access Key。
- 恢复上传路径模板。
- ACL 设置为不发送;无法留空时使用
private。 - 配置图片访问域名或
outputURLPattern。 - 运行
scutil --proxy,按当前网络决定是否填写 HTTP 代理。 - 配置 WebP
quality 80、method 6。 - 将 Amazon S3 设为默认图床。
- 上传测试图片,验证 PicGo 日志、R2 对象和 CDN URL。
- 如有需要,重新安装
~/.local/bin/picgo-r2-check。
已验证案例与日常速查
一次实际优化记录
同一台设备的一次排障记录中,未配置可用代理时:
10:47:19 Uploading...
10:48:05 SUCCESS本次约耗时 46 秒。将 PicGo 代理更新为当时系统正在使用的:
http://127.0.0.1:7897再次测试:
10:54:17 Uploading...
10:54:18 SUCCESS本次约耗时 1 秒。该结果说明当时的瓶颈在直连网络路径,不能视为其他网络、地区或时间都能达到 1 秒的保证。
日常速查
上传突然变慢或失败时:
- 运行
picgo-r2-check。 - 代理端口失败:启动代理软件,重新读取系统代理。
- R2 直连慢而代理快:更新 PicGo 的 HTTP 代理。
- 两条 R2 链路都正常:检查默认上传器、凭据、bucket、ACL 和上传路径。
- 上传成功但 URL 无法访问:检查
cdn.yunshux.com绑定和对象路径。
正常配置的最终目标:
上传器:aws-s3
Bucket:yunshu
Endpoint:https://<ACCOUNT_ID>.r2.cloudflarestorage.com
输出 URL:https://cdn.yunshux.com/<上传路径>