cloudflare-r2 について
Cloudflare R2 オブジェクトストレージを扱うスキルです。ファイル、メディア、バックアップ、モデル成果物を保存する開発者向けに、S3 互換アクセスや Wrangler、Worker バインディング、公開バケットでの操作を説明します。
主な機能
- 01Wrangler によるバケットとオブジェクトの管理
- 02S3 API 経由の R2 アクセス
- 03バケットの Workers へのバインド
- 04公開アクセスとカスタムドメインの設定
- 05ライフサイクルと CORS ルールの設定
活用シーン
- 01ユーザーアップロードやメディアの保存
- 02バックアップや静的アセットの保管
- 03Workers を介したエッジでのファイル配信
- 04機械学習の成果物やトレーニングデータの保存
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bagelhole/ devops-security-agent-skills --skill cloudflare-r2インストール後、Claude Code で/cloudflare-r2を入力して読み込まれたか確認します。
詳細
Cloudflare R2
Cloudflare のグローバルネットワークを基盤とする、エグレス料金が無料の S3 互換オブジェクトストレージです。
使用する場面
- ユーザーのアップロードファイル、メディアファイル、バックアップ、静的アセットを保存する場合。
- 読み取りの多いワークロードでエグレスコストをなくすために AWS S3 を置き換える場合。
- Workers やパブリックバケットアクセスを利用して、エッジからファイルを配信する場合。
- ベンダーロックインを避けるマルチクラウドストレージを構築する場合(S3 API 互換)。
- ML モデルのアーティファクト、トレーニングデータ、推論結果を保存する場合。
前提条件
- R2 を有効にした Cloudflare アカウント(ダッシュボード > R2 > subscribe)。
- Wrangler CLI v3+ がインストールされていること:
npm install -g wrangler。 wrangler loginまたはCLOUDFLARE_API_TOKENを使って認証済みであること。- S3 API にアクセスする場合:R2 > Manage R2 API Tokens で R2 API トークンを作成していること。
Wrangler によるバケット管理
バケットの作成と一覧表示
# Create a new bucket
npx wrangler r2 bucket create app-assets
# Create a bucket in a specific region (hint for data locality)
npx wrangler r2 bucket create eu-uploads --location=eu
# List all buckets
npx wrangler r2 bucket list
# Delete an empty bucket
npx wrangler r2 bucket delete old-bucket
オブジェクトの操作
# Upload a single file
npx wrangler r2 object put app-assets/images/logo.png --file=./logo.png
# Upload with content type
npx wrangler r2 object put app-assets/data/report.json \
--file=./report.json \
--content-type="application/json"
# Download an object
npx wrangler r2 object get app-assets/images/logo.png --file=./downloaded-logo.png
# Delete an object
npx wrangler r2 object delete app-assets/images/old-logo.png
# Get object metadata
npx wrangler r2 object head app-assets/images/logo.png
S3 互換 API アクセス
R2 は S3 API をサポートしているため、既存のツール(AWS CLI、boto3、s3cmd)をそのまま利用できます。
R2 API トークンの生成
- R2 > Manage R2 API Tokens > Create API token に移動します。
- 権限を選択します:Object Read & Write、または Object Read only。
- 可能であれば、特定のバケットに対象を限定します。
- Access Key ID と Secret Access Key を保存します。
AWS CLI の設定
# Configure a named profile for R2
aws configure --profile r2
# Access Key ID: <your-r2-access-key>
# Secret Access Key: <your-r2-secret-key>
# Region: auto
# Output: json
# Use the R2 endpoint
export R2_ENDPOINT="https://<ACCOUNT_ID>.r2.cloudflarestorage.com"
# List buckets
aws s3 ls --endpoint-url=$R2_ENDPOINT --profile=r2
# Sync a directory
aws s3 sync ./dist s3://app-assets/static/ \
--endpoint-url=$R2_ENDPOINT \
--profile=r2
# Copy a file
aws s3 cp ./backup.tar.gz s3://app-assets/backups/backup-$(date +%Y%m%d).tar.gz \
--endpoint-url=$R2_ENDPOINT \
--profile=r2
# List objects with prefix
aws s3 ls s3://app-assets/images/ \
--endpoint-url=$R2_ENDPOINT \
--profile=r2
# Remove objects by prefix
aws s3 rm s3://app-assets/tmp/ --recursive \
--endpoint-url=$R2_ENDPOINT \
--profile=r2
Python boto3 クライアント
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
aws_access_key_id="<R2_ACCESS_KEY>",
aws_secret_access_key="<R2_SECRET_KEY>",
region_name="auto",
)
# Upload file
s3.upload_file("./report.pdf", "app-assets", "reports/report.pdf")
# Generate presigned URL (valid for 1 hour)
url = s3.generate_presigned_url(
"get_object",
Params={"Bucket": "app-assets", "Key": "reports/report.pdf"},
ExpiresIn=3600,
)
print(url)
# List objects
response = s3.list_objects_v2(Bucket="app-assets", Prefix="images/", MaxKeys=100)
for obj in response.get("Contents", []):
print(f"{obj['Key']} - {obj['Size']} bytes")
Worker バインディング
API トークンを使わずにサーバー側からアクセスできるよう、R2 バケットを Workers または Pages Functions にバインドします。
Wrangler の設定
# wrangler.toml
name = "asset-worker"
main = "src/index.ts"
compatibility_date = "2024-09-01"
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "app-assets"
[[r2_buckets]]
binding = "UPLOADS"
bucket_name = "user-uploads"
R2 操作用 Worker
// src/index.ts
interface Env {
ASSETS: R2Bucket;
UPLOADS: R2Bucket;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// GET — serve file from R2
if (request.method === "GET") {
const key = url.pathname.slice(1); // strip leading /
const object = await env.ASSETS.get(key);
if (!object) {
return new Response("Not Found", { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set("etag", object.httpEtag);
headers.set("cache-control", "public, max-age=86400");
return new Response(object.body, { headers });
}
// PUT — upload file to R2
if (request.method === "PUT") {
const key = url.pathname.slice(1);
const contentType = request.headers.get("content-type") || "application/octet-stream";
await env.UPLOADS.put(key, request.body, {
httpMetadata: { contentType },
customMetadata: { uploadedAt: new Date().toISOString() },
});
return new Response(JSON.stringify({ key, status: "uploaded" }), {
headers: { "Content-Type": "application/json" },
});
}
// DELETE — remove file
if (request.method === "DELETE") {
const key = url.pathname.slice(1);
await env.UPLOADS.delete(key);
return new Response(null, { status: 204 });
}
return new Response("Method Not Allowed", { status: 405 });
},
};
Worker での署名付き URL の生成
// Generate time-limited signed URLs using Workers
import { AwsClient } from "aws4fetch";
interface Env {
R2_ACCESS_KEY: string;
R2_SECRET_KEY: string;
R2_ACCOUNT_ID: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const aws = new AwsClient({
accessKeyId: env.R2_ACCESS_KEY,
secretAccessKey: env.R2_SECRET_KEY,
});
const url = new URL(request.url);
const key = url.searchParams.get("key");
if (!key) return new Response("Missing key", { status: 400 });
const r2Url = `https://${env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com/app-assets/${key}`;
const signed = await aws.sign(new Request(r2Url), {
aws: { signQuery: true },
});
return Response.json({ url: signed.url });
},
};
パブリックバケットアクセス
Worker を使わずにファイルを直接配信するには、パブリックアクセスを有効にします。
- R2 > bucket > Settings > Public access に移動します。
- 有効にして、カスタムドメイン(例:
assets.example.com)を設定します。 - オブジェクトには
https://assets.example.com/<key>でアクセスできます。
# Or enable via the r2.dev subdomain (for testing)
# Bucket Settings > R2.dev subdomain > Allow Access
# URL: https://pub-<hash>.r2.dev/<key>
ライフサイクルルール
オブジェクトの有効期限切れや移行を自動化するよう設定します。
# Set lifecycle rules via the Cloudflare dashboard:
# R2 > bucket > Settings > Object lifecycle rules
# Or via API
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2/buckets/app-assets/lifecycle" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{
"id": "expire-tmp-files",
"enabled": true,
"conditions": { "prefix": "tmp/" },
"actions": { "deleteObject": { "daysAfterCreationDate": 7 } }
},
{
"id": "expire-old-logs",
"enabled": true,
"conditions": { "prefix": "logs/" },
"actions": { "deleteObject": { "daysAfterCreationDate": 90 } }
}
]
}'
CORS の設定
# Set CORS policy for browser-based uploads
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2/buckets/app-assets/cors" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"corsRules": [
{
"allowedOrigins": ["https://example.com"],
"allowedMethods": ["GET", "PUT", "HEAD"],
"allowedHeaders": ["Content-Type", "Authorization"],
"maxAgeSeconds": 3600
}
]
}'
トラブルシューティング
| 症状 | 原因 | 対処法 |
|---|---|---|
S3 API 経由で NoSuchBucket エラーが発生する | エンドポイントまたはバケット名が誤っている | エンドポイントが https://<ACCOUNT_ID>.r2.cloudflarestorage.com であることを確認します |
SignatureDoesNotMatch | シークレットキーが誤っているか、エンドポイントが一致していない | R2 API トークンを再生成し、リージョンが auto であることを確認します |
| アップロードは成功するが、GET が 404 を返す | キーのパスが一致していない(先頭にスラッシュがある) | R2 のキーは / で始まらないようにします |
| 大きなファイルのアップロードが遅い | 単一ストリームでアップロードしている | マルチパートアップロードを使用し、wrangler で --expected-size を設定します |
| ブラウザーで CORS エラーが発生する | バケットに CORS 設定がない | オリジンドメインを対象とする CORS ルールを追加します |
Worker バインディングが undefined を返す | wrangler.toml のバインディング名が一致していない | binding 名が Env インターフェースのプロパティ名と一致することを確認します |
| パブリックアクセスで 403 が返る | パブリックアクセスが有効になっていない | バケットの Settings > Public access で有効にします |
関連スキル
- cloudflare-workers - 署名付き URL の生成とエッジでのファイル配信
- cloudflare-pages - R2 バインディングを使った Pages Functions
- cdn-setup - アセット配信用の CDN 設定
コメント
Markdown 対応