R2 Object Storage Reaches General Availability
Cloudflare has announced that R2, its object storage service with zero egress fees, is now generally available. The service, which launched in open beta in May 2022, has attracted more than 12,000 developers during its beta period. Use cases ranged from podcasting and video platforms to ecommerce, with one customer, Vecteezy, previously spending six figures annually on egress fees.
Cloudflare has also used the beta period to migrate its own production workloads to R2. Cloudflare Images, which serves thousands of customers, is now powered by R2.
S3-Compatible API
R2 exposes the familiar S3 API, making it straightforward for developers to migrate existing applications without rewriting them. Basic data operations can be performed using standard S3 SDKs after generating an access key:
// First we import our bindings as usual
import {
S3Client,
ListBucketsCommand,
} from "@aws-sdk/client-s3";
// Then we create a new client. Note that while R2 requires a region for S3 compatibility, only “auto” is supported
const S3 = new S3Client({
region: "auto",
endpoint: `https://${ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: ACCESS_KEY_ID, // fill in your own
secretAccessKey: SECRET_ACCESS_KEY, // fill in your own
},
});
// And now we can use our client to list associated buckets just like we would with any other S3 compatible object storage
console.log(
await S3.send(
new ListBucketsCommand('')
)
);
Examples are also available in Go, Java, PHP, and Ruby. The S3 API is supported across all major languages, so teams can keep their existing tooling and code.
Automatic Region Selection
R2 does not require developers to predict traffic patterns at the outset. Instead of choosing a specific region, the only supported region is auto. When you create a bucket, R2 automatically places it in the closest available region to the request origin. The company says it plans to further optimize placement over time by analyzing data access patterns.
The auto region setting does not conflict with S3 compatibility requirements, as it satisfies the need for a region value while abstracting away the underlying location decision.
Integration with Cloudflare Workers
A key advantage of R2 is its tight coupling with the Workers compute platform. Workers runs on Cloudflare's network of more than 275 locations using isolates rather than containers, avoiding the cold-start latency common in traditional serverless environments.
Developers bind an R2 bucket directly to a Worker and build custom logic around their data without intermediate network hops. A simple REST API over a bucket can be created using the Workers API:
export default {
async fetch(request, env) {
const url = new URL(request.url);
const key = url.pathname.slice(1); // we’ll derive a key from the url path
switch (request.method) {
// For writes, we capture the request body and write that out to our bucket under the associated key
case 'PUT':
await env.MY_BUCKET.put(key, request.body);
return new Response(`Put ${key} successfully!`);
// For reads, we’ll use our key to perform a lookup
case 'GET':
const object = await env.MY_BUCKET.get(key);
// if we don’t find the given key we’ll return a 404 error
if (object === null) {
return new Response('Object Not Found', { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set('etag', object.httpEtag);
return new Response(object.body, {
headers,
});
}
},
};
This lets developers execute logic—such as validation or transformation—directly in the request path before data is served.
Presigned URLs
R2 supports presigned URLs, which delegate permissions for a specific object and a specific action (like upload or download) without exposing the entire bucket. This is useful for applications where developers want end users to interact with storage directly while maintaining security. Example usage:
import {
S3Client,
PutObjectCommand
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const S3 = new S3Client({
region: "auto",
endpoint: `https://${ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: ACCESS_KEY_ID,
secretAccessKey: SECRET_ACCESS_KEY,
},
});
// With getSignedUrl we can produce a custom url with a one hour expiration which will allow our end user to upload their dog pic
console.log(
await getSignedUrl(S3, new PutObjectCommand({Bucket: 'my-bucket-name', Key: 'dog.png'}), { expiresIn: 3600 })
)
Public Buckets
Buckets can be made publicly accessible for unauthenticated requests. When linked to a domain on the Cloudflare account, these buckets can leverage additional Cloudflare features such as Access, Cache, and bot management. This bridges domain-oriented Cloudflare tools with the data stored in R2.
Pricing
R2 charges no egress fees. The pricing model is based on storage volume and two operation classes: Class A (writes, lists) and Class B (reads).
- Storage: $0.015 per GB per month
- Class A operations: $4.50 per million
- Class B operations: $0.36 per million
A free tier is available for developers getting started:
- 10 GB-months of stored data
- 1,000,000 Class A operations per month
- 10,000,000 Class B operations per month
Planned Features
Cloudflare has outlined several capabilities coming to R2:
- Object Lifecycles: Policies for automated management, such as deleting objects sixty days after last access.
- Jurisdictional Restrictions: The ability to constrain data to a jurisdiction (like the EU) for compliance purposes, without introducing traditional regions.
- Live Migration without Downtime: The team is working on extending its existing
Cache reservemechanism so that complete S3 buckets can be migrated to R2.



