Skip to content

Cloudflare R2 -- S3-Compatible Object Storage

DodaTech 4 min read

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

  1. What is the primary cost advantage of R2 compared to AWS S3?
  2. How do you bind an R2 bucket to a Cloudflare Worker in wrangler.toml?
  3. What AWS SDK can be used to interact with R2 with minimal changes?

FAQ

Does R2 support multipart upload?

Yes. R2 supports the S3 Multipart Upload API for objects larger than 100 MB. Use the CreateMultipartUpload, UploadPart, and CompleteMultipartUpload operations. The maximum part count is 10,000 and each part can be up to 5 GB.

How does R2 pricing compare to S3?

R2 charges for storage per GB per month and per operation (read, write, delete). There are no egress fees. AWS S3 charges similar storage rates but adds egress costs ranging from $0.01 to $0.09 per GB depending on volume and region. For read-heavy workloads, R2 can reduce total cost by 50 to 80 percent.

Is R2 data encrypted at rest?

Yes. All data stored in R2 is encrypted at rest using AES-256. Data in transit is encrypted with TLS. R2 also supports customer-managed encryption keys for additional control.

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