Cloudflare R2: The Complete Guide to Free Tier, Pricing, and Setup

Cloudflare R2 is an S3-compatible object storage service built on Cloudflare’s global infrastructure. The single biggest difference compared to AWS S3 or Google Cloud Storage: R2 charges zero egress fees (data transfer out to the internet). This makes R2 the go-to choice for websites that serve files, media, backups, or any application with heavy download traffic.

Cloudflare R2: The Complete Guide to Free Tier, Pricing, and Setup

In this guide, we will cover every aspect: how far the free tier actually goes, how Cloudflare bills you when you exceed limits, and a step-by-step setup tutorial from scratch with working code you can run immediately.

How Far Does the Cloudflare R2 Free Tier Go?

R2 offers one of the most generous free tiers in the object storage industry. More importantly: the free tier never expires. Unlike AWS S3 (free for only the first 12 months), R2 lets you use these limits forever as long as you stay within them.

Monthly free tier limits

ResourceFree per month
Storage (Standard)10 GB-month
Class A operations (write/list)1 million requests
Class B operations (read)10 million requests
Egress (data transfer to the internet)Always free

Important note: Infrequent Access storage has no free tier. If you choose this tier, you are billed from the very first byte. Additionally, delete operations (DeleteObject, DeleteBucket, AbortMultipartUpload) are completely free and do not count toward Class A or Class B.

How far do 10 GB and 10 million requests actually go?

These numbers are enough for most personal projects, portfolios, blogs, and dev/staging environments:

  • Photography portfolio: 500 high-res images at ~10 MB each equals roughly 5 GB. Ten million Class B requests per month easily handles heavy traffic.
  • Static site assets: Blogs, landing pages, and documentation sites serving images and PDFs from R2. Free egress means traffic spikes never inflate your bill.
  • Development environment: Test file uploads, presigned URLs, and S3-compatible integrations without worrying about cost.
  • Important document backups: Contracts, invoices, and records that are rarely accessed but need to be available instantly.

The first limit you will almost always hit is storage (10 GB), not operations. If your project stores 4K video, RAW photos, or large datasets, 10 GB will fill up very quickly.

How Cloudflare R2 Pricing Works in Detail

R2 bills based on two main components: storage and operations. Egress is always free for both free and paid usage.

Paid pricing table (Standard Storage)

ResourcePrice
Standard storage$0.015 / GB-month
Infrequent Access storage$0.01 / GB-month
Class A operations (Standard)$4.50 / million requests
Class B operations (Standard)$0.36 / million requests
Class A operations (Infrequent Access)$9.00 / million requests
Class B operations (Infrequent Access)$0.90 / million requests
Data retrieval (Infrequent Access)$0.01 / GB
Egress (to the internet)Free

What are Class A and Class B operations?

Cloudflare splits operations into two groups priced differently:

Class A (write/list): Actions that write or list data. Priced higher because they consume more resources.

  • PutObject (upload file)
  • CreateMultipartUpload, UploadPart, CompleteMultipartUpload
  • CopyObject
  • ListObjects, ListObjectsV2
  • PutBucketEncryption, PutBucketCors, PutBucketLifecycleConfiguration

Class B (read): Actions that read data or check metadata. Much cheaper.

  • GetObject (download file)
  • HeadObject (check if a file exists, read size, content type)
  • GetObjectRange (download a partial file, byte-range requests)

Delete operations are completely free.

How storage is calculated (GB-month)

Storage is not calculated as “how many GB you are storing right now,” but rather your peak storage each day, averaged over 30 days.

Example:

  • Storing 1 GB continuously for 30 days = 1 GB-month.
  • Storing 1 GB for the first 5 days, then scaling up to 3 GB for the remaining 25 days = (1 × 5/30) + (3 × 25/30) = 2.66 GB-month.

Real-world cost examples

Scenario 1: Small website, 50 GB storage, moderate traffic

CostCalculationAmount
Storage (50 GB)50 × $0.015$0.75
Class A (100K writes)Under 1M, within free tier$0
Class B (2M reads)Under 10M, within free tier$0
Egress (500 GB downloaded)Always free$0
Total$0.75/month

Scenario 2: 1 TB media archive, 10M reads per day

CostCalculationAmount
Storage (1,000 GB)1,000 × $0.015$15.00
Class A (1M writes)Under 1M, within free tier$0
Class B (300M reads/month)(300M – 10M free) × $0.36/million$104.40
Egress (10 TB)Always free$0
Total$119.40/month

Comparison with AWS S3: For the same scenario, S3 egress alone for 10 TB would cost roughly $900 (at $0.09/GB). R2 eliminates that cost entirely. That is why R2 is “cheap” — not because storage is dramatically cheaper, but because egress disappears.

Infrequent Access: when should you use it?

Infrequent Access (IA) has lower storage pricing ($0.01 vs $0.015), but:

  • No free tier.
  • Class A and Class B operations cost twice as much.
  • Retrieval fee of $0.01/GB.
  • Minimum storage duration of 30 days. If you delete earlier, you are still charged for the full 30 days.

IA is suitable for cold data — rarely accessed backups, archives, and old logs. Do not use it for frequently read files.

Step-by-Step Cloudflare R2 Setup Guide

Below is a complete, up-to-date guide based on the current Cloudflare Dashboard interface. You will create a bucket, get credentials, enable public access, and connect with working JavaScript (Node.js) code.

Step 1: Create an account and activate R2

  1. Go to https://dash.cloudflare.com/sign-up and create an account (or log in if you already have one).
  2. In the Dashboard, select Storage & Databases > R2 Object Storage > Overview.
  3. Click Purchase R2 or Get Started to activate. R2 has a free tier, so you do not need to enter a credit card immediately to begin.

Step 2: Create a bucket

  1. In the R2 Overview, click Create Bucket.
  2. Bucket name: Enter a globally unique name, for example my-project-assets-2026. Use only lowercase letters, numbers, and hyphens.
  3. Location: Choose Automatic to let Cloudflare distribute automatically, or select the region closest to your users (APAC for Southeast Asia).
  4. Click Create Bucket.

Step 3: Enable Public Access (optional but recommended)

If you want end users to access files directly via URL (images, videos, PDFs), you need to enable public access:

  1. Go into your newly created bucket and select the Settings tab.
  2. Scroll down to Public Development URL (r2.dev).
  3. Click Enable. A popup will ask you to type allow to confirm.
  4. Once enabled, you will receive a URL like: https://pub-xxxxxxxxxxxxx.r2.dev. Save this — it is the base URL for public file access.

Or use a Custom Domain (advanced): In the Public Access tab, click Connect Domain, enter your domain (for example cdn.yoursite.com), and follow the DNS instructions. Custom domains offer better SEO and branding than r2.dev.

Step 4: Create an API Token and retrieve credentials

This is the most important step for connecting R2 from code or CLI.

  1. In the R2 Overview, click Manage API Tokens (or go to bucket > Settings > R2 API Tokens).
  2. Click Create API Token.
  3. Token name: Choose a memorable name, for example my-app-r2-access.
  4. Permissions: Select Admin Read & Write if your app needs to upload and download. Select Admin Read only if you only need to read.
  5. TTL: Set to Forever for production use, or set an expiration date for higher security.
  6. Click Create API Token.
  7. COPY IMMEDIATELY the following three values (they are shown only once):
    • Access Key ID
    • Secret Access Key
    • Account ID

Get your S3 API Endpoint:

Your endpoint follows this format:

https://{ACCOUNT_ID}.r2.cloudflarestorage.com

Replace {ACCOUNT_ID} with your copied Account ID. Example:

https://a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6.r2.cloudflarestorage.com

Step 5: Upload and download files with Node.js (working code)

Install the AWS SDK v3 (R2 is S3-compatible):

npm init -y
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

Create a file named r2.js:

import { S3Client, PutObjectCommand, GetObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { readFileSync } from "fs";

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

const BUCKET_NAME = process.env.R2_BUCKET_NAME;

// 1. Upload a file to R2
async function uploadFile(filePath, key) {
  const fileBuffer = readFileSync(filePath);
  
  const command = new PutObjectCommand({
    Bucket: BUCKET_NAME,
    Key: key,
    Body: fileBuffer,
    ContentType: "image/jpeg", // Adjust based on file type
  });

  await R2.send(command);
  console.log(`Uploaded: ${key}`);
}

// 2. Generate a presigned URL for sharing (time-limited)
async function getPresignedUrl(key, expiresIn = 3600) {
  const command = new GetObjectCommand({
    Bucket: BUCKET_NAME,
    Key: key,
  });

  const url = await getSignedUrl(R2, command, { expiresIn });
  console.log(`Presigned URL (expires in ${expiresIn}s): ${url}`);
  return url;
}

// 3. List files in the bucket
async function listFiles() {
  const command = new ListObjectsV2Command({
    Bucket: BUCKET_NAME,
    MaxKeys: 10,
  });

  const response = await R2.send(command);
  console.log("Files:", response.Contents?.map(c => c.Key) || []);
}

// 4. Run demo
async function main() {
  try {
    await uploadFile("./photo.jpg", "uploads/photo.jpg");
    await getPresignedUrl("uploads/photo.jpg", 3600);
    await listFiles();
  } catch (err) {
    console.error("Error:", err);
  }
}

main();

Create a .env file in the same directory:

R2_ACCOUNT_ID=your-account-id-here
R2_ACCESS_KEY_ID=your-access-key-id
R2_SECRET_ACCESS_KEY=your-secret-access-key
R2_BUCKET_NAME=your-bucket-name

Run it:

node r2.js

If you enabled public access via r2.dev or a custom domain, files can be accessed directly at:

https://pub-xxxxxxxxxxxxx.r2.dev/uploads/photo.jpg

Step 6: Connect R2 with Wrangler CLI (optional)

If you are using Cloudflare Workers, Wrangler CLI is the best way to interact with R2:

# Install Wrangler
npm install -g wrangler

# Log in
npx wrangler login

# Create a bucket
npx wrangler r2 bucket create my-bucket

# Upload a file
npx wrangler r2 object put my-bucket/file.txt --file ./file.txt

# List files
npx wrangler r2 object list my-bucket

Step 7: Configuration with popular frameworks

Next.js (App Router):

// app/api/upload/route.js
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  region: "auto",
  endpoint: process.env.R2_ENDPOINT,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
  },
});

export async function POST(request) {
  const formData = await request.formData();
  const file = formData.get("file");
  const buffer = Buffer.from(await file.arrayBuffer());

  await s3.send(new PutObjectCommand({
    Bucket: process.env.R2_BUCKET_NAME,
    Key: `uploads/${file.name}`,
    Body: buffer,
    ContentType: file.type,
  }));

  return Response.json({ 
    url: `${process.env.R2_PUBLIC_URL}/uploads/${file.name}` 
  });
}

WordPress (with Offload Media plugin):

  1. Install the Offload Media or WP Offload Media plugin.
  2. Go to Settings and select Cloudflare R2 as the provider.
  3. Enter your Account ID, Access Key, and Secret Key.
  4. Select your created bucket.
  5. Click Verify Credentials and then Save.

Important Things to Remember When Using R2

  • Egress is free, but Class B is not free beyond the limit. Every time a user views an image on R2, that is one GetObject = one Class B request. Under 10 million per month it is free, but if your site serves hundreds of millions of impressions, Class B will start billing.
  • There is no zero-latency guarantee. R2 is not an edge-to-edge CDN cache like Cloudflare Images. Files are stored on R2 and served through the Cloudflare network, but the first read from a new edge location may be slightly slower than a cache hit.
  • The r2.dev subdomain should not be used for long-term production. Attach a custom domain for full URL control and to avoid dependency on a Cloudflare subdomain.
  • Infrequent Access has no free tier. Do not switch to IA if you are trying to stay within the free tier.
  • Delete operations are free. You can delete files freely without being charged for operations.

Conclusion

Cloudflare R2 is one of the best object storage options for indie developers, startups, and anyone who needs to serve files with predictable costs. The free tier of 10 GB + 10 million Class B requests + permanently free egress is enough to run a portfolio, blog, dev environment, or even a small SaaS in the early stages.

The real strength of R2 is not that storage is slightly cheaper than S3 (it is only marginally so), but that egress fees are eliminated entirely — the cost category that typically makes up 80-90% of a traditional object storage bill. If your application has many users downloading files, viewing media, or accessing assets frequently, R2 can reduce your monthly bill from hundreds of dollars down to just a few.

Start by creating a bucket, running the Node.js code above, and uploading your first file. If you have used S3 before, migrating to R2 requires almost no code changes — just swap the endpoint and credentials.

Comments


  • No comments yet.

Web-Based Tools

Press Ctrl + \ on desktop, or swipe left anywhere on mobile.

Login