Cloudflare R2 -- S3-Compatible Object Storage
In this tutorial, you will learn how Cloudflare R2 offers S3-compatible object storage without egress charges, enabling cost-effective storage for global applications directly integrated with Workers. Zero egress fees are important because traditional cloud object storage providers charge for data transfer out of their network, which can dominate total storage costs for read-heavy workloads. A real-world example is a media platform serving video thumbnails to millions of users worldwide without paying egress costs per request.
Why R2 Matters
Traditional object storage services like AWS S3 charge egress fees every time data leaves their network. For read-heavy applications -- image hosting, video streaming, CDN origins -- these egress costs often exceed storage costs by a factor of ten or more. Cloudflare R2 eliminates egress fees entirely, charging only for storage and operations. Data is served through Cloudflare's global network of over 330 data centers, meaning users download objects from the nearest edge location with minimal latency. R2 is API-compatible with S3, so existing S3 SDKs and tools work with minimal configuration changes.
R2 Architecture
flowchart TD
U[User] --> G[Cloudflare Network 330+ PoPs]
G --> B[R2 Bucket]
B --> S[Storage Backend]
S --> R[Redundant Replicas]
W[Workers Runtime] -->|env.R2 binding| B
T[S3-Compatible Tools] -->|S3 API| B
style B fill:#f90,color:#fff
style W fill:#3498db,color:#fff
style T fill:#2ecc71,color:#fff
R2 buckets are accessible via two paths: the S3 API for external tools and the Workers R2 binding for edge compute. Objects are stored redundantly across multiple storage nodes within the Cloudflare network.
Creating an R2 Bucket
# Create a new R2 bucket using Wrangler
npx wrangler r2 bucket create media-assets
# Expected output:
# Creating bucket media-assets...
# Bucket created successfully
Buckets are globally unique within your Cloudflare account. Bucket names must be between 3 and 63 characters, lowercase alphanumeric with hyphens only.
Binding R2 to a Worker
// wrangler.toml
// [[r2_buckets]]
// binding = "MEDIA_BUCKET"
// bucket_name = "media-assets"
// worker.js -- list objects in the bucket
export default {
async fetch(request, env) {
const objects = await env.MEDIA_BUCKET.list();
const names = objects.objects.map(o => o.key);
return new Response(JSON.stringify({
bucket: 'media-assets',
objects: names,
total: objects.objects.length
}), {
headers: { 'Content-Type': 'application/json' }
});
}
};
// Expected response:
// {"bucket": "media-assets", "objects": ["logo.png", "banner.jpg", "icon.svg"], "total": 3}
The list() method returns metadata about objects in the bucket without downloading them. Use the prefix and delimiter options to paginate and simulate folder structures.
Uploading Objects with the S3 API
# Using boto3 (AWS SDK for Python) with R2 endpoint
import boto3
client = boto3.client(
's3',
endpoint_url='https://<account-id>.r2.cloudflarestorage.com',
aws_access_key_id='<access-key-id>',
aws_secret_access_key='<secret-access-key>'
)
with open('logo.png', 'rb') as f:
client.put_object(
Bucket='media-assets',
Key='images/logo.png',
Body=f,
ContentType='image/png'
)
# Expected output:
# {"ResponseMetadata": {"HTTPStatusCode": 200}}
R2's S3-compatible endpoint uses the standard S3 API. Generate an API token in the Cloudflare dashboard with R2 permissions to obtain your access key and secret.
Common Errors and Troubleshooting
Bucket Name Already Taken
R2 bucket names are globally unique across all Cloudflare accounts. If the name is taken, choose a more specific name, such as including your project or organization name.
Authentication Failed
S3 API calls require valid access keys with R2 permissions. Generate a token in the Cloudflare dashboard under R2, not under the API Tokens section. Ensure the token has at least read-write permissions for the target bucket.
Object Size Limits
R2 supports individual objects up to 5 TB. For uploads larger than 100 MB, use multipart upload to split the file into parts and upload them in parallel.
CORS Errors in Browser
R2 buckets do not allow public browser access by default. For browser-based uploads, configure CORS on the bucket or proxy requests through a Worker.
List Results Truncated
The list() method returns up to 1000 objects by default. Use pagination with the cursor parameter to retrieve additional results.
Practice Questions
- What is the primary cost advantage of R2 compared to AWS S3?
- How do you bind an R2 bucket to a Cloudflare Worker in wrangler.toml?
- What AWS SDK can be used to interact with R2 with minimal changes?
FAQ
Summary
Cloudflare R2 provides S3-compatible object storage with zero egress fees, making it ideal for read-heavy global applications. Buckets are created with Wrangler and bound to Workers via the [[r2_buckets]] configuration. R2's S3 API compatibility means existing tools like boto3 work with minimal changes. Combined with Workers, R2 enables building complete Data Pipelines at the edge without worrying about bandwidth costs.
This guide is brought to you by the developers of Cloudflare, Amazon S3, and Durga Antivirus Pro at DodaTech.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro