Cloudflare R2 Free Tier: The Complete Guide to Zero-Egress Object Storage

Cloudflare R2 Free Tier: The Complete Guide to Zero-Egress Object Storage

Learn how to use Cloudflare R2's generous free tier — 10GB storage, 10M reads/month, and zero egress fees. Setup guide, use cases, and alternatives compared.

Cloudflare R2StorageObject StorageFree TierS3Tutorial

Cloudflare R2 Free Tier: Zero Egress, Real Storage, No Credit Card Trap

If you’ve ever been burned by a surprise AWS S3 bill because users downloaded your images too many times, Cloudflare R2 exists specifically to fix that problem. It’s object storage that doesn’t charge you for bandwidth — ever. And the free tier is genuinely usable for real projects, not just toy demos.

This guide walks through what’s actually included, where R2 shines, how to set it up from scratch, and how it compares to the alternatives most developers consider first.


What is Cloudflare R2?

Cloudflare R2 is S3-compatible object storage that runs on Cloudflare’s global network. The two things that make it different from every other S3 alternative:

  1. Zero egress fees. You pay nothing when files are downloaded, regardless of volume.
  2. Native S3 API compatibility. Existing AWS SDKs, CLI tools, and libraries work with minimal changes — usually just swapping the endpoint URL.

R2 launched publicly in 2022 and has steadily added features that used to be S3-only: lifecycle rules, presigned URLs, CORS configuration, custom domains, and integration with Cloudflare Workers. It’s now a realistic drop-in replacement for S3 in most applications.

The pitch is simple: store files, serve files, never get a bandwidth bill. For a directory like stacksfree.com that tracks genuinely free tools, R2 is one of the few “free tier that scales” options in the storage category.


Key Free Tier Features & Limits

Cloudflare R2’s free tier is permanent (not a 12-month trial like AWS) and includes:

Resource Free Monthly Allowance
Storage 10 GB-month
Class A operations (writes, lists) 1,000,000
Class B operations (reads) 10,000,000
Egress bandwidth Unlimited ($0)

What counts as Class A vs Class B

  • Class A (write-heavy): PutObject, ListBucket, CreateMultipartUpload, CompleteMultipartUpload, CopyObject. These are the operations that mutate state or scan buckets.
  • Class B (read-heavy): GetObject, HeadObject. Simple reads and metadata checks.

The 10:1 ratio of reads to writes on the free tier matches most web app workloads, where content is consumed far more often than it’s produced.

What happens when you exceed limits

Cloudflare doesn’t cut you off — you simply start paying the usage rates:

  • Storage: $0.015 / GB-month
  • Class A ops: $4.50 / million
  • Class B ops: $0.36 / million
  • Egress: still $0

This is the part that makes R2’s pricing model fundamentally different from competitors. Even when you outgrow free, your cost stays predictable because the biggest variable — bandwidth — is always zero.


Best Use Cases (with Real Examples)

1. User-Generated Content Uploads

Profile pictures, document attachments, PDF exports. A typical SaaS app might let users upload a 2MB avatar. With R2:

  • Upload via presigned URL directly from the browser (no proxy through your server)
  • Serve through a custom domain like cdn.yourapp.com
  • Egress stays free even if a power user gets viewed 100,000 times

Real-world pattern: A community forum with 5,000 active users uploading screenshots. Storage stays well under 10GB. Reads can spike to millions per month during viral threads — with S3, that’s a real bill; with R2, it’s still $0.

2. Image and Static Asset Hosting

If you’re building a blog, marketing site, or documentation portal, R2 replaces the “stick images in the repo and hope” anti-pattern. Upload once, reference via URL, and let Cloudflare’s CDN handle delivery.

Pair it with Cloudflare Images or Imgix-style on-the-fly transforms (via Workers) for resizing, and you have a full media pipeline without AWS.

3. Database and File Backups

Nightly PostgreSQL dumps, MySQL snapshots, log archives. The Class A limit of 1M writes/month means you can comfortably write thousands of backup files per day. Storing 10GB of compressed backups covers a substantial database history.

Practical setup: A cron job runs pg_dump | gzip | aws s3 cp - s3://your-bucket/backup-$(date +%F).sql.gz pointing at your R2 endpoint. Rotate old files with lifecycle rules.

4. Migrating Off AWS S3

This is the most common production use case. The migration path:

  1. Use rclone or aws s3 sync to copy your existing bucket to R2
  2. Update your application’s endpoint URL from s3.amazonaws.com to <account-id>.r2.cloudflarestorage.com
  3. Keep the same SDK, same IAM-style credentials, same code

For most apps, the change is a single environment variable. The bandwidth savings are immediate and permanent.


Step-by-Step Setup Guide

Step 1: Create a Cloudflare account

Go to dash.cloudflare.com and sign up. You don’t need an existing domain registered with Cloudflare to use R2 — but having one makes custom domain setup trivial later.

Important: Add a payment method even on the free tier. Cloudflare requires it to verify you’re a real account, but you won’t be charged unless you explicitly exceed free limits and they’re enabled for billing.

Step 2: Enable R2

From the dashboard sidebar, click R2 Object Storage. Accept the terms. You’ll land on the R2 overview page.

Step 3: Create your first bucket

  1. Click Create bucket
  2. Name it (must be globally unique, lowercase, DNS-safe — e.g., my-app-uploads-prod)
  3. Pick a location hint if you care about latency:
    • Automatic (default) — Cloudflare picks the closest region to your first write
    • North Americawnam (Western) or enam (Eastern)
    • Europeweur or eeur
    • Asia Pacificapac
  4. Click Create

Step 4: Generate API credentials

R2 uses S3-compatible auth, so you’ll need an access key ID and secret.

  1. Go to R2Manage R2 API Tokens
  2. Click Create API Token
  3. Choose permissions:
    • Object Read & Write for most app use cases
    • Admin Read & Write only if you need bucket management
  4. Specify which buckets the token can access (or all buckets)
  5. Click Create API Token

You’ll see:

  • Access Key ID
  • Secret Access Key
  • Endpoint URL: https://<account-id>.r2.cloudflarestorage.com
  • Jurisdiction-specific endpoint if applicable

Save these immediately. The secret is only shown once.

Step 5: Upload files

Using the AWS CLI

Configure a profile:

aws configure --profile r2
# Access Key ID: <your-access-key>
# Secret Access Key: <your-secret-key>
# Default region: auto
# Default output: json

Upload a file:

aws s3 cp photo.jpg \
  s3://my-app-uploads-prod/images/photo.jpg \
  --endpoint-url https://<account-id>.r2.cloudflarestorage.com \
  --profile r2

Using the AWS SDK (Node.js example)

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

const s3 = new S3Client({
  region: 'auto',
  endpoint: 'https://<account-id>.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-app-uploads-prod',
  Key: 'images/photo.jpg',
  Body: fileBuffer,
  ContentType: 'image/jpeg',
}));

Generating a presigned URL (for direct browser uploads)

import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { PutObjectCommand } from '@aws-sdk/client-s3';

const url = await getSignedUrl(s3, new PutObjectCommand({
  Bucket: 'my-app-uploads-prod',
  Key: 'uploads/user-123-avatar.png',
  ContentType: 'image/png',
}), { expiresIn: 3600 });

// POST the file directly to this URL from the browser

Step 6: Serve files publicly

By default, R2 buckets are private. To make files publicly accessible:

Option A — Enable public access via r2.dev subdomain

  1. Go to your bucket → Settings
  2. Under Public access, enable the r2.dev subdomain
  3. Files become available at https://pub-<hash>.r2.dev/<key>

This is fine for testing but not recommended for production — the URL is ugly and Cloudflare may rate-limit it.

Option B — Custom domain (recommended)

  1. Under Public access, click Connect Domain
  2. Enter your domain (e.g., cdn.yourdomain.com)
  3. Cloudflare auto-configures the DNS record if the domain is on their nameservers
  4. Files serve at https://cdn.yourdomain.com/images/photo.jpg

Custom domains get full Cloudflare CDN caching, HTTPS, and analytics for free.


How R2 Compares to Alternatives

Cloudflare R2 vs AWS S3

Feature Cloudflare R2 AWS S3
Free storage 10 GB forever 5 GB for 12 months only
Egress fees $0 ~$0.09/GB after 100GB
Class A ops (free) 1M/month 2,000 PUT/month
S3 API compatibility Yes Native
Multi-region replication Coming/limited Mature
Ecosystem maturity Growing Industry standard

Verdict: If you’re starting a new project, R2 is the obvious choice unless you need deep AWS integrations (Lambda triggers on S3 events, Macie, Glacier archival tiers). Migrating existing apps is low-risk because the SDK works identically.

User sentiment: The most common praise on Hacker News and Reddit r/aws threads: “My S3 bill dropped dramatically after moving media to R2.” The most common complaint: occasional S3 API edge cases (particularly around multipart uploads and IAM policy features) aren’t fully implemented yet.

Cloudflare R2 vs Backblaze B2

Feature Cloudflare R2 Backblaze B2
Free storage 10 GB 10 GB
Free Class A ops 1M/month 2,500/day (~75K/month)
Egress Free Free up to 3x stored data/day
S3-compatible API Yes Yes (via S3-compatible endpoint)
Pricing beyond free $0.015/GB $0.006/GB + egress

Verdict: B2 has cheaper raw storage but the egress fees kill it for media-heavy apps. B2’s free egress allowance (3x stored data) sounds generous until you realize 30GB of daily egress on the 10GB free tier vanishes instantly if a single image goes viral. R2 wins for anything user-facing.

Cloudflare R2 vs Cloudinary

These solve different problems, but developers often compare them for image hosting:

Feature Cloudflare R2 Cloudinary
Purpose Raw object storage Image/video transformation + CDN
Free storage 10 GB ~25 GB (credit-based)
Free bandwidth Unlimited ~25 GB/month
On-the-fly transforms Via Workers (DIY) Built-in
Pricing model Simple per-GB Credit-based, complex

Verdict: Cloudinary is better if you need automatic resizing, format conversion, and AI-powered transformations out of the box. R2 is better if you just need a place to put files and serve them fast. For many apps, the combination of R2 + a Cloudflare Worker doing image transforms replaces Cloudinary entirely — and stays free at much higher volumes.


Tips to Maximize the Free Tier

1. Enforce upload size limits client-side and server-side

The 10GB storage cap can disappear fast if users upload uncompressed video. Implement:

// Hard cap at 5MB before upload
if (file.size > 5 * 1024 * 1024) {
  throw new Error('File too large');
}

And validate content-type — don’t trust the extension.

2. Compress and transform before storing

For images:

  • Use sharp (Node.js) or Pillow (Python) to resize on upload
  • Convert PNG → WebP for 25-35% size reduction (WebP docs)
  • Strip EXIF data unless you need it
import sharp from 'sharp';

const optimized = await sharp(fileBuffer)
  .resize(1200, null, { withoutEnlargement: true })
  .webp({ quality: 80 })
  .toBuffer();

A typical 4MB phone photo becomes a 150KB WebP — meaning your 10GB free tier holds ~65,000 images instead of ~2,500.

3. Set aggressive cache headers

R2 files served through Cloudflare’s CDN cache for free. Set long TTLs:

await s3.send(new PutObjectCommand({
  Bucket: 'my-bucket',
  Key: 'assets/logo.png',
  Body: buffer,
  ContentType: 'image/png',
  CacheControl: 'public, max-age=31536000, immutable',
}));

Cached responses don’t count against your Class B operation quota because they never hit R2.

4. Use lifecycle rules for cleanup

Set up automatic deletion of temporary files (upload previews, expired exports):

  1. Bucket → SettingsObject lifecycle rules
  2. Rule: Delete objects with prefix tmp/ after 7 days

This prevents orphaned files from slowly eating your storage budget.

5. Avoid unnecessary ListObjects calls

ListBucket is a Class A operation. Don’t run directory listings on every page load. Cache the file manifest in your database or use Cloudflare KV instead.

6. Use Workers for auth-gated access

If you need to gate file access behind user authentication, don’t proxy through your server (that counts as a read on every request). Instead:

  • Put a Cloudflare Worker in front of your bucket
  • The Worker validates a JWT or session cookie
  • If valid, it streams the file from R2 to the user

Workers have their own free tier (100K requests/day) and R2 access from Workers is free.

7. Monitor usage in the dashboard

Cloudflare’s R2 dashboard shows storage and operation counts in real-time. Check it weekly during your first month to catch runaway usage before it becomes a bill.


The Bottom Line

Cloudflare R2’s free tier is one of the most genuinely useful free offerings in the storage space. 10GB of storage, 10 million reads, and zero egress fees covers a wide range of real applications — from hobby projects to small SaaS products — without ever asking for a credit card.

The S3 compatibility means you’re not locked into Cloudflare’s ecosystem. If you ever outgrow the free tier, paid pricing is transparent and the bandwidth-is-free model protects you from the unpredictable bills that make AWS such a gamble for indie projects.

For the full profile and comparison with other free-tier storage options, check the Cloudflare R2 listing on stacksfree.com.

References

[1] dash.cloudflare.com [2] WebP docs [3] Cloudflare R2 listing on stacksfree.com