mcpField
C
cloudflare-r2

cloudflare-r2

github.com/jezwebクラウド・DevOps1.1k462 回インストール3 週間前に更新
リポジトリ

cloudflare-r2 について

cloudflare-r2 は、Cloudflare R2 オブジェクトストレージと Workers を利用する開発者向けの実践的なスキルです。オブジェクト操作、アップロード方式、アクセス設定、運用上の注意に加え、Wrangler と S3 互換の署名付き URL を解説します。

主な機能

  1. 01R2 Workers のオブジェクト操作
  2. 02マルチパートと署名付きアップロード
  3. 03CORS とメタデータの設定
  4. 04一括削除と範囲読み取り
  5. 05Wrangler の設定ガイド
  6. 06セキュリティと信頼性の実践

活用シーン

  1. 01大容量または再開可能なファイルのアップロード
  2. 02署名付き URL によるブラウザーからのアップロード
  3. 03バケットの CORS とメタデータ設定
  4. 04ストレージのエラーやレート制限への対応

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jezweb/claude-skills --skill cloudflare-r2

インストール後、Claude Code で/cloudflare-r2を入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
.claude-plugin/
plugin.json
references/
common-patterns.md
s3-compatibility.md
workers-api.md
rules/
cloudflare-r2.md
templates/
r2-cors-config.json
r2-multipart-upload.ts
r2-presigned-urls.ts
r2-simple-upload.ts

詳細

Cloudflare R2 オブジェクトストレージ

ステータス:本番環境対応 ✅ 最終更新日:2026-01-20 依存関係:cloudflare-worker-base(Worker のセットアップ用) 最新バージョン:[email protected], @cloudflare/[email protected], [email protected]

最近の更新(2025年):

  • 2025年9月:R2 SQL のオープンベータ(Apache Iceberg 向けサーバーレスクエリエンジン)、Pipelines の GA(リアルタイムストリームの取り込み)、リモートバインディングの GA(ローカル開発環境からデプロイ済みの R2 に接続)
  • 2025年5月:ダッシュボードの再設計(ディープリンク対応、バケット設定の一元化)、Super Slurper が 5 倍高速化(Workers/Queues/Durable Objects を使って再構築)
  • 2025年4月:R2 Data Catalog のオープンベータ(Apache Iceberg のマネージドカタログ)、Event Notifications のオープンベータ(Queue ごとに毎秒 5,000 メッセージ)
  • 2025年:バケット上限の引き上げ(最大 100 万)、CRC-64/NVME チェックサム、顧客キーによるサーバー側暗号化、Infrequent Access ストレージクラス(ベータ)、オセアニアリージョン、S3 API の機能強化(sha256/sha1 チェックサム、ListParts、条件付き CopyObject)

クイックスタート(5 分)

# 1. Create bucket
npx wrangler r2 bucket create my-bucket

# 2. Add binding to wrangler.jsonc
# {
#   "r2_buckets": [{
#     "binding": "MY_BUCKET",
#     "bucket_name": "my-bucket",
#     "preview_bucket_name": "my-bucket-preview"  // Optional: separate dev/prod
#   }]
# }

# 3. Upload/download from Worker
type Bindings = { MY_BUCKET: R2Bucket };

// Upload
await env.MY_BUCKET.put('file.txt', data, {
  httpMetadata: { contentType: 'text/plain' }
});

// Download
const object = await env.MY_BUCKET.get('file.txt');
if (!object) return c.json({ error: 'Not found' }, 404);

return new Response(object.body, {
  headers: {
    'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
    'ETag': object.httpEtag,
  },
});

# 4. Deploy
npx wrangler deploy

R2 Workers API

基本メソッド
// put() - Upload objects
await env.MY_BUCKET.put('file.txt', data, {
  httpMetadata: {
    contentType: 'text/plain',
    cacheControl: 'public, max-age=3600',
  },
  customMetadata: { userId: '123' },
  md5: await crypto.subtle.digest('MD5', data),  // Checksum verification
});

// Conditional upload (prevent overwrites)
const object = await env.MY_BUCKET.put('file.txt', data, {
  onlyIf: { uploadedBefore: new Date('2020-01-01') }
});
if (!object) return c.json({ error: 'File already exists' }, 409);

// get() - Download objects
const object = await env.MY_BUCKET.get('file.txt');
if (!object) return c.json({ error: 'Not found' }, 404);

const text = await object.text();           // As string
const json = await object.json();           // As JSON
const buffer = await object.arrayBuffer();  // As ArrayBuffer

// Range requests (partial downloads)
const partial = await env.MY_BUCKET.get('video.mp4', {
  range: { offset: 0, length: 1024 * 1024 }  // First 1MB
});

// head() - Get metadata only (no body download)
const object = await env.MY_BUCKET.head('file.txt');
console.log(object.size, object.etag, object.customMetadata);

// delete() - Delete objects
await env.MY_BUCKET.delete('file.txt');  // Single delete (idempotent)
await env.MY_BUCKET.delete(['file1.txt', 'file2.txt']);  // Bulk delete (max 1000)

// list() - List objects
const listed = await env.MY_BUCKET.list({
  prefix: 'images/',  // Filter by prefix
  limit: 100,
  cursor: cursor,     // Pagination
  delimiter: '/',     // Folder-like listing
  include: ['httpMetadata', 'customMetadata'],  // IMPORTANT: Opt-in for metadata
});

for (const object of listed.objects) {
  console.log(`${object.key}: ${object.size} bytes`);
  console.log(object.httpMetadata?.contentType);  // Now populated with include parameter
  console.log(object.customMetadata);             // Now populated with include parameter
}

マルチパートアップロード

100 MB を超えるファイルや、再開可能なアップロードに使用します。大きなファイル、ブラウザーからのアップロード、並列化が必要な場合に利用します。

// 1. Create multipart upload
const multipart = await env.MY_BUCKET.createMultipartUpload('large-file.zip', {
  httpMetadata: { contentType: 'application/zip' }
});

// 2. Upload parts (5MB-100MB each, max 10,000 parts)
const multipart = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
const part1 = await multipart.uploadPart(1, chunk1);
const part2 = await multipart.uploadPart(2, chunk2);

// 3. Complete upload
const object = await multipart.complete([
  { partNumber: 1, etag: part1.etag },
  { partNumber: 2, etag: part2.etag },
]);

// 4. Abort if needed
await multipart.abort();

制限:パートは 5 MB~100 MB、アップロードごとの最大パート数は 10,000 です。5 MB 未満のファイルには使用しないでください(オーバーヘッドが発生します)。


署名付き URL

クライアントが R2 へ直接アップロード/ダウンロードできるようにします(Worker を経由しません)。aws4fetch ライブラリを使用します。

import { AwsClient } from 'aws4fetch';

const r2Client = new AwsClient({
  accessKeyId: env.R2_ACCESS_KEY_ID,
  secretAccessKey: env.R2_SECRET_ACCESS_KEY,
});

const url = new URL(
  `https://${bucketName}.${accountId}.r2.cloudflarestorage.com/${filename}`
);
url.searchParams.set('X-Amz-Expires', '3600');  // 1 hour expiry

const signed = await r2Client.sign(
  new Request(url, { method: 'PUT' }),  // or 'GET' for downloads
  { aws: { signQuery: true } }
);

// Client uploads directly to R2
await fetch(signed.url, { method: 'PUT', body: file });

重大なセキュリティ上の注意:

  • ❌ 絶対に R2 のアクセスキーをクライアント側のコードに公開しないでください
  • ✅ 必ず 署名付き URL をサーバー側で生成してください
  • ✅ 必ず 有効期限を設定してください(通常は 1~24 時間)
  • ✅ 必ず URL を生成する前に認証を追加してください
  • ✅ ユーザーフォルダーに範囲を限定することを検討してください:users/${userId}/${filename}
署名付き URL のドメイン要件

重大な注意:署名付き URL が機能するのは S3 API ドメインのみで、カスタムドメインでは機能しません。

// ❌ WRONG - Presigned URLs don't work with custom domains
const url = new URL(`https://cdn.example.com/${filename}`);
const signed = await r2Client.sign(
  new Request(url, { method: 'PUT' }),
  { aws: { signQuery: true } }
);
// This URL will fail - presigning requires S3 domain

// ✅ CORRECT - Use R2 storage domain for presigned URLs
const url = new URL(
  `https://${accountId}.r2.cloudflarestorage.com/${filename}`
);
const signed = await r2Client.sign(
  new Request(url, { method: 'PUT' }),
  { aws: { signQuery: true } }
);

// Pattern: Upload via presigned S3 URL, serve via custom domain
async function generateUploadUrl(filename: string) {
  const uploadUrl = new URL(
    `https://${accountId}.r2.cloudflarestorage.com/${filename}`
  );
  const signed = await r2Client.sign(
    new Request(uploadUrl, { method: 'PUT' }),
    { aws: { signQuery: true } }
  );

  return {
    uploadUrl: signed.url,  // For client upload (S3 domain)
    publicUrl: `https://cdn.example.com/${filename}`  // For serving (custom domain)
  };
}

出典:コミュニティの知見

Wrangler の API トークン要件

⚠️ Wrangler CLI には「Admin Read & Write」権限が必要です。「Object Read & Write」ではありません。

Wrangler の操作用に API トークンを作成する場合:

  • ✅ 使用する:R2 → Admin Read & Write
  • ❌ 使用しない:R2 → Object Read & Write(403 Forbidden エラーが発生します)

理由:「Object Read & Write」は S3 API からの直接アクセス専用です。バケット操作には、Wrangler の管理者レベルの権限が必要です。

# With wrong permissions:
export CLOUDFLARE_API_TOKEN="token_with_object_readwrite"
wrangler r2 object put my-bucket/file.txt --file=./file.txt --remote
# ✘ [ERROR] Failed to fetch - 403: Forbidden

# With correct permissions (Admin Read & Write):
wrangler r2 object put my-bucket/file.txt --file=./file.txt --remote
# ✔ Success

出典:GitHub Issue #9235


CORS の設定

ブラウザーからアクセスする前に、バケット設定(Dashboard → R2 → Bucket → Settings → CORS Policy)で CORS を設定してください。

Dashboard 形式と CLI 形式

⚠️ wrangler CLI と Dashboard UI では、異なる CORS 形式が使用されます。これはよく混乱の原因になります。

Dashboard 形式(UI のみで使用できます):

[{
  "AllowedOrigins": ["https://example.com"],
  "AllowedMethods": ["GET", "PUT"],
  "AllowedHeaders": ["*"],
  "ExposeHeaders": ["ETag"],
  "MaxAgeSeconds": 3600
}]

CLI 形式(wrangler r2 bucket cors で必要です):

{
  "rules": [{
    "allowed": {
      "origins": ["https://www.example.com"],
      "methods": ["GET", "PUT"],
      "headers": ["Content-Type", "Authorization"]
    },
    "exposeHeaders": ["ETag", "Content-Length"],
    "maxAgeSeconds": 8640
  }]
}
# Using CLI format
wrangler r2 bucket cors set my-bucket --file cors-config.json

# Error if using Dashboard format:
# "The CORS configuration file must contain a 'rules' array"

出典:GitHub Issue #10076

カスタムドメインの CORS

R2 でカスタムドメインを使用する場合、CORS は2 つのレイヤーで処理されます:

  1. R2 バケットの CORS:すべてのアクセス方法(署名付き URL、S3 への直接アクセス)に適用されます
  2. Transform Rules の CORS:カスタムドメインの Cloudflare Cache 設定を通じて、追加の CORS ヘッダーを設定します
// Bucket CORS (set via dashboard or wrangler)
{
  "rules": [{
    "allowed": {
      "origins": ["https://app.example.com"],
      "methods": ["GET", "PUT"],
      "headers": ["Content-Type"]
    },
    "maxAgeSeconds": 3600
  }]
}

// Additional CORS via Transform Rules (Dashboard → Rules → Transform Rules)
// Modify Response Header: Access-Control-Allow-Origin: https://app.example.com

// Order of CORS evaluation:
// 1. R2 bucket CORS (if presigned URL or direct R2 access)
// 2. Transform Rules CORS (if via custom domain)

出典:コミュニティの知見

署名付き URL の場合:CORS は R2 が直接処理します(Worker ではなく、バケットで設定してください)。


HTTP メタデータとカスタムメタデータ

// HTTP metadata (standard headers)
await env.MY_BUCKET.put('file.pdf', data, {
  httpMetadata: {
    contentType: 'application/pdf',
    cacheControl: 'public, max-age=31536000, immutable',
    contentDisposition: 'attachment; filename="report.pdf"',
    contentEncoding: 'gzip',
  },
  customMetadata: {
    userId: '12345',
    version: '1.0',
  }  // Max 2KB total, keys/values must be strings
});

// Read metadata
const object = await env.MY_BUCKET.head('file.pdf');
console.log(object.httpMetadata, object.customMetadata);

エラー処理

よくある R2 のエラー
try {
  await env.MY_BUCKET.put(key, data);
} catch (error: any) {
  const message = error.message;

  if (message.includes('R2_ERROR')) {
    // Generic R2 error
  } else if (message.includes('exceeded')) {
    // Quota exceeded
  } else if (message.includes('precondition')) {
    // Conditional operation failed
  } else if (message.includes('multipart')) {
    // Multipart upload error
  }

  console.error('R2 Error:', message);
  return c.json({ error: 'Storage operation failed' }, 500);
}
リトライロジック

R2では運用上の問題により、2025年第1四半期に大規模な障害が2回発生しました(2月6日:59分間、3月21日:1時間7分間)。プラットフォームエラーに対して、指数バックオフを用いた堅牢なリトライロジックを実装してください。

async function r2WithRetry<T>(
  operation: () => Promise<T>,
  maxRetries = 5
): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error: any) {
      const message = error.message;

      // Retry on transient errors and platform issues
      const is5xxError =
        message.includes('500') ||
        message.includes('502') ||
        message.includes('503') ||
        message.includes('504');

      const isRetryable =
        is5xxError ||
        message.includes('network') ||
        message.includes('timeout') ||
        message.includes('temporarily unavailable');

      if (!isRetryable || attempt === maxRetries - 1) {
        throw error;
      }

      // Exponential backoff (longer for platform errors)
      // 5xx errors: 1s, 2s, 4s, 8s, 16s (up to 31s total)
      // Other errors: 1s, 2s, 4s, 5s, 5s (up to 17s total)
      const delay = is5xxError
        ? Math.min(1000 * Math.pow(2, attempt), 16000)
        : Math.min(1000 * Math.pow(2, attempt), 5000);

      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw new Error('Max retries exceeded');
}

// Usage
const object = await r2WithRetry(() =>
  env.MY_BUCKET.get('important-file.txt')
);

プラットフォームの信頼性:R2は一般的に信頼性の高いサービスですが、2025年第1四半期の障害は、本番アプリケーションにおけるリトライロジックの重要性を示しています。すべての5xxエラーは、指数バックオフを用いて再試行してください。

情報源:


パフォーマンスの最適化

// Batch delete (up to 1000 keys)
await env.MY_BUCKET.delete(['file1.txt', 'file2.txt', 'file3.txt']);

// Range requests for large files
const partial = await env.MY_BUCKET.get('video.mp4', {
  range: { offset: 0, length: 10 * 1024 * 1024 }  // First 10MB
});

// Cache headers for immutable assets
await env.MY_BUCKET.put('static/app.abc123.js', jsData, {
  httpMetadata: { cacheControl: 'public, max-age=31536000, immutable' }
});

// Checksums for data integrity
const md5Hash = await crypto.subtle.digest('MD5', fileData);
await env.MY_BUCKET.put('important.dat', fileData, { md5: md5Hash });
同時書き込みのレート制限

⚠️ 同じオブジェクトキーへの高頻度な同時書き込みは、HTTP 429のレート制限を引き起こします。

// ❌ BAD: Multiple Workers writing to same key rapidly
async function logToSharedFile(env: Env, logEntry: string) {
  const existing = await env.LOGS.get('global-log.txt');
  const content = (await existing?.text()) || '';
  await env.LOGS.put('global-log.txt', content + logEntry);
  // High write frequency to same key = 429 errors
}

// ✅ GOOD: Shard by timestamp or ID (distribute writes)
async function logWithSharding(env: Env, logEntry: string) {
  const timestamp = Date.now();
  const shard = Math.floor(timestamp / 60000); // 1-minute shards

  await env.LOGS.put(`logs/${shard}.txt`, logEntry, {
    customMetadata: { timestamp: timestamp.toString() }
  });
  // Different keys = no rate limiting
}

// ✅ ALTERNATIVE: Use Durable Objects for append operations
// Durable Objects can handle high-frequency updates to same state

// ✅ ALTERNATIVE: Use Queues + batch processing
// Buffer writes and batch them with unique keys

情報源:R2の制限に関するドキュメント

R2.devドメインのレート制限

🚨 重大:{bucket}.{account}.r2.cloudflarestorage.com(r2.dev)ドメインは、本番環境での使用を想定していません。

r2.devの制限:

  • ❌ レート制限は変動します(毎秒数百リクエスト程度から開始)
  • ❌ 帯域幅が制限されます
  • ❌ SLAやパフォーマンスの保証はありません
  • ❌ 負荷がかかると429 Too Many Requestsが返されます

本番環境では、必ずカスタムドメインを使用してください

// ❌ NOT for production - r2.dev endpoint
const publicUrl = `https://${bucketName}.${accountId}.r2.cloudflarestorage.com/${key}`;
// This will be rate limited in production

// ✅ Production: Custom domain
const productionUrl = `https://cdn.example.com/${key}`;

// Setup custom domain:
// 1. Dashboard → R2 → Bucket → Settings → Custom Domains
// 2. Add your domain (e.g., cdn.example.com)
// 3. Benefits:
//    - No rate limiting beyond account limits
//    - Cloudflare Cache support
//    - Custom cache rules via Workers
//    - Full CDN features

r2.devはテスト/開発専用です。本番環境ではカスタムドメインが必須です。

情報源:R2の制限に関するドキュメント


ベストプラクティスのまとめ

必ず行うこと:

  • すべてのアップロードでcontentTypeを設定する
  • 複数のオブジェクトを削除する場合は、バッチ削除を使用する(最大1000個)
  • 静的アセットにはキャッシュヘッダーを設定する
  • クライアントから大きなファイルをアップロードする場合は、署名付きURLを使用する(S3ドメインのみ)
  • 100MBを超えるファイルにはマルチパートを使用する
  • ブラウザーからのアップロード前にCORSを設定する(wranglerではCLI形式を使用する)
  • 署名付きURLに有効期限を設定する(1~24時間)
  • メタデータのみが必要な場合はhead()を使用する
  • 上書きを防ぐため、条件付き操作を使用する
  • 本番環境ではカスタムドメインを使用する(r2.devは使用しない)
  • レート制限を避けるため、書き込みを複数のキーに分散する
  • メタデータを取得するには、list()でincludeパラメーターを使用する
  • 5xxエラーに対して、指数バックオフを用いたリトライロジックを実装する

決して行わないこと:

  • R2のアクセスキーをクライアント側のコードに公開しない
  • contentTypeの設定を省略しない(ファイルがバイナリとしてダウンロードされます)
  • ループ内で削除しない(バッチ削除を使用してください)
  • ブラウザーからのアップロードでCORSの設定を省略しない
  • 小さなファイル(5MB未満)にマルチパートを使用しない
  • 1回の呼び出しで1000を超えるキーを削除しない
  • 署名付きURLの有効期限設定を省略しない(セキュリティリスクになります)
  • 本番環境でr2.devドメインを使用しない(レート制限があります)
  • カスタムドメインで署名付きURLを使用しない(S3ドメインを使用してください)
  • 同じキーに高頻度で書き込まない(429エラーの原因になります)
  • wranglerで「Object Read & Write」トークンを使用しない(「Admin Read & Write」を使用してください)
マルチテナントアーキテクチャ

バケットの上限がアカウントあたり100万バケットに引き上げられたことで、大規模アプリケーションでもテナントごとのバケットを利用できるようになりました。

// Option 1: Per-tenant buckets (now scalable to 1M tenants)
const bucketName = `tenant-${tenantId}`;
const bucket = env[bucketName]; // Dynamic binding

// Option 2: Key prefixing (still preferred for most use cases)
await env.MY_BUCKET.put(`tenants/${tenantId}/file.txt`, data);

// Choose based on:
// - Per-tenant buckets: Strong isolation, separate billing/quotas
// - Key prefixing: Simpler, fewer resources, easier to manage

情報源:R2の制限に関するドキュメント


防止される既知の問題

このスキルは、文書化されている 13 件の問題を防止します。

Issue #問題エラー防止策
#1ブラウザーでの CORS エラーブラウザーでアップロード/ダウンロードできないバケット設定で CORS を構成し、正しい CLI 形式を使用します
#2ファイルがバイナリとしてダウンロードされるcontent-type が設定されていないアップロード時に必ず httpMetadata.contentType を設定します
#3署名付き URL の有効期限URL が期限切れにならない必ず X-Amz-Expires を設定します(1~24 時間)
#4マルチパートアップロードの制限パートが上限を超えるパートは 5MB~100MB にし、最大 10,000 パートにします
#5一括削除の制限1000 件を超えるキーの削除に失敗する削除対象を 1000 件ずつのバッチに分割します
#6カスタムメタデータの超過2KB の上限を超えるカスタムメタデータを 2KB 未満に抑えます
#7list() でメタデータが欠落するhttpMetadata が undefined になるinclude: ['httpMetadata', 'customMetadata'] パラメーターを使用します(Issue #10870)
#8CORS 形式の混同「'rules' 配列を含める必要があります」wrangler では rules ラッパーを含む CLI 形式を使用します(Issue #10076)
#9API トークンの 403 エラー「取得に失敗しました - 403」wrangler では「Object Read & Write」ではなく「Admin Read & Write」を使用します(Issue #9235)
#10r2.dev のレート制限本番環境で HTTP 429 が発生するカスタムドメインを使用し、本番環境では r2.dev を決して使用しません(R2 Limits)
#11同時書き込みによる 429 エラー同じキーに頻繁に書き込む書き込みを異なるキーに分散します(R2 Limits)
#12署名付き URL のドメインエラー署名付き URL が失敗するカスタムドメインではなく、S3 ドメインのみを使用します(Community)
#13プラットフォームの停止停止中に 5xx エラーが発生する指数バックオフを使用したリトライロジックを実装します(Feb 6、Mar 21)

開発のベストプラクティス

ローカル R2 ストレージのクリーンアップ

⚠️ ローカル R2 の DELETE 操作では、blob ファイルがクリーンアップされません。wrangler dev の使用時に削除したオブジェクトは .wrangler/state/v3/r2/{bucket-name}/blobs/ に残り、ローカルストレージが際限なく増加します。

# Symptom: .wrangler/state grows large during development
du -sh .wrangler/state/v3/r2/

# Fix: Manually cleanup local R2 storage
rm -rf .wrangler/state/v3/r2/

# Alternative: Use remote R2 for development
wrangler dev --remote

出典:GitHub Issue #10795

リモート R2 アクセスの問題

⚠️ --remote を使ったローカル開発では、.get() 操作が不安定になることがあります。put() は正しく動作するにもかかわらず、get() が undefined を返すという報告があります。

# If experiencing issues with remote R2 in local dev:

# Option 1: Use local buckets instead (recommended)
wrangler dev  # No --remote flag

# Option 2: Deploy to preview environment for testing
wrangler deploy --env preview

# Option 3: Add retry logic if must use --remote
async function safeGet(bucket: R2Bucket, key: string) {
  for (let i = 0; i < 3; i++) {
    const obj = await bucket.get(key);
    if (obj && obj.body) return obj;
    await new Promise(r => setTimeout(r, 1000));
  }
  throw new Error('Failed to get object after retries');
}

出典:GitHub Issue #8868(コミュニティからの情報)


Wrangler コマンドのリファレンス

# Bucket management
wrangler r2 bucket create <BUCKET_NAME>
wrangler r2 bucket list
wrangler r2 bucket delete <BUCKET_NAME>

# Object management
wrangler r2 object put <BUCKET_NAME>/<KEY> --file=<FILE_PATH>
wrangler r2 object get <BUCKET_NAME>/<KEY> --file=<OUTPUT_PATH>
wrangler r2 object delete <BUCKET_NAME>/<KEY>

# List objects
wrangler r2 object list <BUCKET_NAME>
wrangler r2 object list <BUCKET_NAME> --prefix="folder/"

公式ドキュメント


R2 への保存を始めましょう! 🚀

最終確認日:2026-01-20 | スキルのバージョン:2.0.0 | 変更点:コミュニティ調査で判明した既知の問題を 7 件追加しました(list() のメタデータ、CORS 形式の混同、API トークンの権限、r2.dev のレート制限、同時書き込みの制限、署名付き URL のドメイン要件、プラットフォーム停止時のリトライパターン)。5xx エラーに対するリトライロジックを強化し、開発のベストプラクティスのセクションを追加し、バケットの上限を 1M に引き上げたことを記載しました。

コメント