Back to Skills

cloudflare-r2

Elios-FPT
Updated Today
14 views
1
View on GitHub
Designapidesigndata

About

This Claude Skill provides implementation guidance for Cloudflare R2, an S3-compatible object storage service with zero egress fees. Use it when building file storage features, migrating data to/from R2, configuring buckets, or integrating with Cloudflare Workers. It covers R2's APIs, SDKs, and key capabilities like lifecycle management and CORS configuration.

Documentation

Cloudflare R2

S3-compatible object storage with zero egress bandwidth fees. Built on Cloudflare's global network for high durability (11 nines) and strong consistency.

When to Use This Skill

  • Implementing object storage for applications
  • Migrating from AWS S3 or other storage providers
  • Setting up file uploads/downloads
  • Configuring public or private buckets
  • Integrating R2 with Cloudflare Workers
  • Using R2 with S3-compatible tools and SDKs
  • Configuring CORS, lifecycles, or event notifications
  • Optimizing storage costs with zero egress fees

Prerequisites

Required:

  • Cloudflare account with R2 purchased
  • Account ID from Cloudflare dashboard

For API access:

  • R2 Access Keys (Access Key ID + Secret Access Key)
  • Generate from: Cloudflare Dashboard → R2 → Manage R2 API Tokens

For Wrangler CLI:

npm install -g wrangler
wrangler login

API Key Configuration

R2 requires specific credentials for S3-compatible API access. The system searches for API keys in this order:

  1. process.env - Runtime environment variables
  2. <project-root>/.env - Project-level environment file
  3. .claude/.env - Claude configuration directory
  4. .claude/skills/.env - Skills shared configuration
  5. .claude/skills/cloudflare-r2/.env - Skill-specific configuration

Required Environment Variables:

# R2-specific credentials
R2_ACCESS_KEY_ID=your_r2_access_key_id_here
R2_SECRET_ACCESS_KEY=your_r2_secret_access_key_here
R2_ACCOUNT_ID=your_account_id_here

# Or use general Cloudflare credentials
CLOUDFLARE_ACCOUNT_ID=your_account_id_here
CLOUDFLARE_API_TOKEN=your_api_token_here  # For Wrangler CLI

Where to Get Credentials:

  • R2 Access Keys: Cloudflare Dashboard → R2 → Manage R2 API Tokens → Create API Token
    • Generates both Access Key ID and Secret Access Key (S3-compatible)
  • Account ID: Cloudflare Dashboard → Overview → Account ID (right sidebar)
  • API Token (for Wrangler): Cloudflare Dashboard → My Profile → API Tokens

Example .env File:

# See .claude/skills/.env.example for complete configuration
R2_ACCESS_KEY_ID=abc123...
R2_SECRET_ACCESS_KEY=xyz789...
R2_ACCOUNT_ID=def456...

Core Concepts

Architecture

  • S3-compatible API - works with AWS SDKs and tools
  • Workers API - native Cloudflare Workers integration
  • Global network - strong consistency across all regions
  • Zero egress fees - no bandwidth charges for data retrieval

Storage Classes

  • Standard - default, optimized for frequent access
  • Infrequent Access - lower storage cost, retrieval fees apply, 30-day minimum

Access Methods

  1. R2 Workers Binding - serverless integration (recommended for new apps)
  2. S3 API - compatibility with existing tools
  3. Public buckets - direct HTTP access via custom domains or r2.dev
  4. Presigned URLs - temporary access without credentials

Quick Start

1. Create Bucket

Wrangler:

wrangler r2 bucket create my-bucket

With location hint:

wrangler r2 bucket create my-bucket --location=wnam

Locations: wnam (West NA), enam (East NA), weur (West EU), eeur (East EU), apac (Asia Pacific)

2. Upload Object

Wrangler:

wrangler r2 object put my-bucket/file.txt --file=./local-file.txt

Workers API:

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

3. Download Object

Wrangler:

wrangler r2 object get my-bucket/file.txt --file=./downloaded.txt

Workers API:

const object = await env.MY_BUCKET.get('file.txt');
const contents = await object.text();

Workers Integration

Binding Configuration

wrangler.toml:

[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
preview_bucket_name = "my-bucket-preview"

Common Operations

Upload with metadata:

await env.MY_BUCKET.put('user-uploads/photo.jpg', imageData, {
  httpMetadata: {
    contentType: 'image/jpeg',
    cacheControl: 'public, max-age=31536000',
  },
  customMetadata: {
    uploadedBy: userId,
    uploadDate: new Date().toISOString(),
  },
});

Download with streaming:

const object = await env.MY_BUCKET.get('large-file.mp4');
if (object === null) {
  return new Response('Not found', { status: 404 });
}

return new Response(object.body, {
  headers: {
    'Content-Type': object.httpMetadata.contentType,
    'ETag': object.etag,
  },
});

List objects:

const listed = await env.MY_BUCKET.list({
  prefix: 'user-uploads/',
  limit: 100,
});

for (const object of listed.objects) {
  console.log(object.key, object.size);
}

Delete object:

await env.MY_BUCKET.delete('old-file.txt');

Check if object exists:

const object = await env.MY_BUCKET.head('file.txt');
if (object) {
  console.log('Exists:', object.size, 'bytes');
}

S3 SDK Integration

AWS CLI

Configure:

aws configure
# Access Key ID: <your-key-id>
# Secret Access Key: <your-secret>
# Region: auto

Operations:

# List buckets
aws s3api list-buckets --endpoint-url https://<accountid>.r2.cloudflarestorage.com

# Upload file
aws s3 cp file.txt s3://my-bucket/ --endpoint-url https://<accountid>.r2.cloudflarestorage.com

# Generate presigned URL (expires in 1 hour)
aws s3 presign s3://my-bucket/file.txt --endpoint-url https://<accountid>.r2.cloudflarestorage.com --expires-in 3600

JavaScript (AWS SDK v3)

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  region: "auto",
  endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
  },
});

await s3.send(new PutObjectCommand({
  Bucket: "my-bucket",
  Key: "file.txt",
  Body: fileContents,
}));

Python (Boto3)

import boto3

s3 = boto3.client(
    service_name="s3",
    endpoint_url=f'https://{account_id}.r2.cloudflarestorage.com',
    aws_access_key_id=access_key_id,
    aws_secret_access_key=secret_access_key,
    region_name="auto",
)

# Upload file
s3.upload_fileobj(file_obj, 'my-bucket', 'file.txt')

# Download file
s3.download_file('my-bucket', 'file.txt', './local-file.txt')

Rclone (Large Files)

Configure:

rclone config
# Select: Amazon S3 → Cloudflare R2
# Enter credentials and endpoint

Upload with multipart optimization:

# For large files (>100MB)
rclone copy large-video.mp4 r2:my-bucket/ \
  --s3-upload-cutoff=100M \
  --s3-chunk-size=100M

Public Buckets

Enable Public Access

Wrangler:

wrangler r2 bucket create my-public-bucket
# Then enable in dashboard: R2 → Bucket → Settings → Public Access

Access URLs

r2.dev (development only, rate-limited):

https://pub-<hash>.r2.dev/file.txt

Custom domain (recommended for production):

  1. Dashboard → R2 → Bucket → Settings → Public Access
  2. Add custom domain
  3. Cloudflare handles DNS/TLS automatically

CORS Configuration

Required for:

  • Browser-based uploads
  • Cross-origin API calls
  • Presigned URL usage from web apps

Wrangler:

wrangler r2 bucket cors put my-bucket --rules '[
  {
    "AllowedOrigins": ["https://example.com"],
    "AllowedMethods": ["GET", "PUT", "POST"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3600
  }
]'

Important: Origins must match exactly (no trailing slash).

Multipart Uploads

For files >100MB or parallel uploads:

Workers API:

const multipart = await env.MY_BUCKET.createMultipartUpload('large-file.mp4');

// Upload parts (5MiB - 5GiB each, max 10,000 parts)
const part1 = await multipart.uploadPart(1, chunk1);
const part2 = await multipart.uploadPart(2, chunk2);

// Complete upload
const object = await multipart.complete([part1, part2]);

Constraints:

  • Part size: 5MiB - 5GiB
  • Max parts: 10,000
  • Max object size: 5TB
  • Incomplete uploads auto-abort after 7 days (configurable via lifecycle)

Data Migration

Sippy (Incremental, On-Demand)

Best for: Gradual migration, avoiding upfront egress fees

# Enable for bucket
wrangler r2 bucket sippy enable my-bucket \
  --provider=aws \
  --bucket=source-bucket \
  --region=us-east-1 \
  --access-key-id=$AWS_KEY \
  --secret-access-key=$AWS_SECRET

Objects migrate when first requested. Subsequent requests served from R2.

Super Slurper (Bulk, One-Time)

Best for: Complete migration, known object list

  1. Dashboard → R2 → Data Migration → Super Slurper
  2. Select source provider (AWS, GCS, Azure)
  3. Enter credentials and bucket name
  4. Start migration

Lifecycle Rules

Auto-delete or transition storage classes:

Wrangler:

wrangler r2 bucket lifecycle put my-bucket --rules '[
  {
    "action": {"type": "AbortIncompleteMultipartUpload"},
    "filter": {},
    "abortIncompleteMultipartUploadDays": 7
  },
  {
    "action": {"type": "Transition", "storageClass": "InfrequentAccess"},
    "filter": {"prefix": "archives/"},
    "daysFromCreation": 90
  }
]'

Event Notifications

Trigger Workers on bucket events:

Wrangler:

wrangler r2 bucket notification create my-bucket \
  --queue=my-queue \
  --event-type=object-create

Supported events:

  • object-create - new uploads
  • object-delete - deletions

Message format:

{
  "account": "account-id",
  "bucket": "my-bucket",
  "object": {"key": "file.txt", "size": 1024, "etag": "..."},
  "action": "PutObject",
  "eventTime": "2024-01-15T12:00:00Z"
}

Best Practices

Performance

  • Use Cloudflare Cache with custom domains for frequently accessed objects
  • Multipart uploads for files >100MB (faster, more reliable)
  • Rclone for batch operations (concurrent transfers)
  • Location hints match user geography

Security

  • Never commit Access Keys to version control
  • Use environment variables for credentials
  • Bucket-scoped tokens for least privilege
  • Presigned URLs for temporary access
  • Enable Cloudflare Access for additional protection

Cost Optimization

  • Infrequent Access storage for archives (30+ day retention)
  • Lifecycle rules to auto-transition or delete
  • Larger multipart chunks = fewer Class A operations
  • Monitor usage via dashboard analytics

Naming

  • Bucket names: lowercase, hyphens, 3-63 chars
  • Avoid sequential prefixes for better performance (e.g., use hashed prefixes)
  • No dots in bucket names if using custom domains with TLS

Limits

  • Buckets per account: 1,000
  • Object size: 5TB max
  • Bucket name: 3-63 characters
  • Lifecycle rules: 1,000 per bucket
  • Event notification rules: 100 per bucket
  • r2.dev rate limit: 1,000 req/min (use custom domains for production)

Troubleshooting

401 Unauthorized:

  • Verify Access Keys are correct
  • Check endpoint URL includes account ID
  • Ensure region is "auto" for most operations

403 Forbidden:

  • Check bucket permissions and token scopes
  • Verify CORS configuration for browser requests
  • Confirm bucket exists and name is correct

404 Not Found:

  • Object key case-sensitive
  • Check bucket name spelling
  • Verify object was uploaded successfully

Presigned URLs not working:

  • Verify CORS configuration
  • Check URL expiry time
  • Ensure origin matches CORS rules exactly

Multipart upload failures:

  • Part size must be 5MiB - 5GiB
  • Max 10,000 parts per upload
  • Complete upload within 7 days (or configure lifecycle)

Reference Files

For detailed documentation, see:

  • references/api-reference.md - Complete API endpoint documentation
  • references/sdk-examples.md - SDK examples for all languages
  • references/workers-patterns.md - Advanced Workers integration patterns
  • references/pricing-guide.md - Detailed pricing and cost optimization

Additional Resources

Quick Install

/plugin add https://github.com/Elios-FPT/EliosCodePracticeService/tree/main/cloudflare-r2

Copy and paste this command in Claude Code to install this skill

GitHub 仓库

Elios-FPT/EliosCodePracticeService
Path: .claude/skills/cloudflare-r2

Related Skills

langchain

Meta

LangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.

View skill

evaluating-llms-harness

Testing

This Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.

View skill

business-rule-documentation

Meta

This skill provides standardized templates for systematically documenting business logic and domain knowledge following Domain-Driven Design principles. It helps developers capture business rules, process flows, decision trees, and terminology glossaries to maintain consistency between requirements and implementation. Use it when documenting domain models, creating business rule repositories, or bridging communication between business and technical teams.

View skill

llamaindex

Meta

LlamaIndex is a data framework for building RAG-powered LLM applications, specializing in document ingestion, indexing, and querying. It provides key features like vector indices, query engines, and agents, and supports over 300 data connectors. Use it for document Q&A, chatbots, and knowledge retrieval when building data-centric applications.

View skill