Overview
Cloudflare R2 Storage provides object storage without egress fees. Store large amounts of unstructured data with S3-compatible APIs.
Bucket Management
Create a Bucket
Create a new R2 bucket:
wrangler r2 bucket create < nam e > [--location < hin t > ] [--storage-class < clas s > ]
Options:
<name> - Bucket name (3-63 characters, lowercase, numbers, hyphens)
--location <hint> - Geographic placement hint
weur - Western Europe
eeur - Eastern Europe
apac - Asia Pacific
oc - Oceania
wnam - Western North America
enam - Eastern North America
--storage-class, -s - Default storage class for uploads
--jurisdiction, -J - Data residency jurisdiction (e.g., eu, fedramp)
--update-config - Automatically add to wrangler.json
--name <binding> - Custom binding name
Examples:
# Create a basic bucket
wrangler r2 bucket create my-bucket
# Create with location hint
wrangler r2 bucket create my-bucket --location enam
# Create with jurisdiction
wrangler r2 bucket create eu-bucket --jurisdiction eu
# Create with storage class
wrangler r2 bucket create archive-bucket --storage-class InfrequentAccess
Configuration:
{
"r2_buckets" : [
{
"binding" : "MY_BUCKET" ,
"bucket_name" : "my-bucket"
}
]
}
List Buckets
View all R2 buckets in your account:
wrangler r2 bucket list [--jurisdiction < jurisdictio n > ]
Example Output:
name: my-bucket
created: 2024-01-15T10:30:00Z
location: enam
object_count: 1,234
name: archive-bucket
created: 2024-02-01T14:20:00Z
location: weur
object_count: 567
Get Bucket Info
Retrieve detailed information about a bucket:
wrangler r2 bucket info < bucke t > [--jurisdiction < jurisdictio n > ] [--json]
Options:
<bucket> - Bucket name
--jurisdiction, -J - Bucket jurisdiction
--json - Output as JSON
Example:
wrangler r2 bucket info my-bucket
Output:
name: my-bucket
created: 2024-01-15T10:30:00.000Z
location: enam
default_storage_class: Standard
object_count: 1,234
bucket_size: 5.2 GB
Update Bucket
Update the default storage class of a bucket:
wrangler r2 bucket update storage-class < nam e > --storage-class < clas s >
Options:
<name> - Bucket name
--storage-class, -s - New default storage class (required)
--jurisdiction, -J - Bucket jurisdiction
Example:
wrangler r2 bucket update storage-class my-bucket --storage-class InfrequentAccess
Delete a Bucket
Delete an empty R2 bucket:
wrangler r2 bucket delete < bucke t > [--jurisdiction < jurisdictio n > ]
The bucket must be empty before deletion. Delete all objects first.
Object Operations
Put an Object Upload a file to R2: wrangler r2 object put < bucke t > / < ke y > --file < pat h >
Options:
<bucket>/<key> - Destination path (format: bucket/key)
--file, -f - Local file to upload
--pipe, -p - Read from stdin instead of file
--content-type, --ct - MIME type
--content-disposition, --cd - Presentational info
--content-encoding, --ce - Encoding applied to object
--content-language, --cl - Content language
--cache-control, --cc - Caching behavior
--expires - Expiration date/time
--storage-class, -s - Storage class for this object
--jurisdiction, -J - Bucket jurisdiction
--local - Upload to local R2 simulation
--persist-to - Local persistence directory
Examples: # Upload a file
wrangler r2 object put my-bucket/images/logo.png --file ./logo.png
# Upload with content type
wrangler r2 object put my-bucket/data.json --file ./data.json --content-type application/json
# Upload from stdin
cat data.txt | wrangler r2 object put my-bucket/data.txt --pipe
# Upload with cache control
wrangler r2 object put my-bucket/static/app.js --file ./app.js --cache-control "max-age=3600"
# Upload with storage class
wrangler r2 object put my-bucket/archive/old.zip --file ./old.zip --storage-class InfrequentAccess
Get an Object Download a file from R2: wrangler r2 object get < bucke t > / < ke y > [--file < destinatio n > ]
Options:
<bucket>/<key> - Source path
--file, -f - Destination file path
--pipe, -p - Write to stdout
--jurisdiction, -J - Bucket jurisdiction
--local - Use local R2 simulation
--persist-to - Local persistence directory
Examples: # Download to current directory (uses key name)
wrangler r2 object get my-bucket/images/logo.png
# Download to specific file
wrangler r2 object get my-bucket/data.json --file ./output.json
# Pipe to stdout
wrangler r2 object get my-bucket/data.txt --pipe
# Pipe to another command
wrangler r2 object get my-bucket/archive.tar.gz --pipe | tar -xz
Delete an Object Remove an object from R2: wrangler r2 object delete < bucke t > / < ke y >
Options:
<bucket>/<key> - Object path to delete
--jurisdiction, -J - Bucket jurisdiction
--local - Use local R2 simulation
--persist-to - Local persistence directory
Example: wrangler r2 object delete my-bucket/old-file.txt
Bulk Operations
Bulk Upload (Experimental)
Upload multiple files efficiently:
wrangler r2 bulk put < bucke t > --filename < mapping-fil e > [--concurrency < n > ]
Options:
<bucket> - Target bucket name
--filename, -f - JSON file with key/file mappings
--concurrency - Concurrent uploads (default: 20)
All standard put options (content-type, cache-control, etc.)
Mapping File Format:
[
{
"key" : "images/photo1.jpg" ,
"file" : "./uploads/photo1.jpg"
},
{
"key" : "images/photo2.jpg" ,
"file" : "./uploads/photo2.jpg"
}
]
Example:
wrangler r2 bulk put my-bucket --filename ./uploads.json --concurrency 50
Advanced Features
CORS Configuration Configure Cross-Origin Resource Sharing: wrangler r2 bucket cors put < bucke t > --rules < fil e >
View CORS rules: wrangler r2 bucket cors get < bucke t >
Delete CORS rules: wrangler r2 bucket cors delete < bucke t >
Custom Domains Connect a custom domain to your bucket: wrangler r2 bucket domain add < bucke t > < domai n >
List domains: wrangler r2 bucket domain list < bucke t >
Remove domain: wrangler r2 bucket domain remove < bucke t > < domai n >
Lifecycle Rules Manage object lifecycle: wrangler r2 bucket lifecycle put < bucke t > --rules < fil e >
View lifecycle: wrangler r2 bucket lifecycle get < bucke t >
Delete lifecycle: wrangler r2 bucket lifecycle delete < bucke t >
Object Locking Enable WORM (Write Once Read Many): wrangler r2 bucket lock enable < bucke t >
Get lock status: wrangler r2 bucket lock get < bucke t >
Worker Integration
Access R2 from your Worker:
export default {
async fetch ( request , env ) {
const bucket = env . MY_BUCKET ;
// Upload an object
await bucket . put ( "file.txt" , "Hello World" , {
httpMetadata: {
contentType: "text/plain" ,
cacheControl: "max-age=3600"
},
customMetadata: {
uploadedBy: "user-123"
}
});
// Download an object
const object = await bucket . get ( "file.txt" );
if ( object === null ) {
return new Response ( "Not found" , { status: 404 });
}
// Return object with correct headers
return new Response ( object . body , {
headers: {
"Content-Type" : object . httpMetadata . contentType || "application/octet-stream" ,
"Cache-Control" : object . httpMetadata . cacheControl || ""
}
});
// List objects
const list = await bucket . list ({ prefix: "uploads/" });
const keys = list . objects . map ( obj => obj . key );
// Delete an object
await bucket . delete ( "old-file.txt" );
return new Response ( JSON . stringify ( keys ));
}
} ;
Local Development
Test R2 operations locally:
# Use local simulation
wrangler r2 object put my-bucket/test.txt --file ./test.txt --local
# Get from local storage
wrangler r2 object get my-bucket/test.txt --local
# Specify persistence directory
wrangler r2 object put my-bucket/file.txt --file ./file.txt --local --persist-to ./.wrangler/state
Local R2 data is stored in .wrangler/state/v3/r2/ by default.
Storage Classes
R2 offers different storage classes for cost optimization:
Standard - Default, frequent access, lowest latency
InfrequentAccess - Less frequent access, lower storage cost
Set storage class during upload or as bucket default.
Best Practices
Naming
Use lowercase, numbers, and hyphens
Bucket names: 3-63 characters
Avoid dots for SSL/TLS compatibility
Use meaningful, descriptive names
Performance
Use appropriate storage classes
Enable caching with Cache-Control headers
Consider custom domains for production
Use bulk operations for multiple files
Organization
Use prefixes to organize objects: images/, videos/
Implement consistent naming conventions
Use metadata for searchability
Plan key structure before scaling
Security
Use jurisdictions for compliance
Configure CORS when needed
Implement access controls in Workers
Use presigned URLs for temporary access
Limits and Quotas
Max object size : 5 TB (via multipart upload)
Max put size : 300 MB (single request)
Bucket name : 3-63 characters
No egress fees : Unlimited data transfer out