R2 Storage Overview
R2 là gì?
S3-Compatible Object Storage
┌─────────────────────────────────────────────────────────────┐
│ CLOUDFLARE R2 │
├─────────────────────────────────────────────────────────────┤
│ │
│ S3-compatible object storage without egress fees │
│ │
│ Features: │
│ - S3 API compatibility │
│ - Zero egress fees │
│ - Global distribution │
│ - Workers binding │
│ - Public bucket support │
│ │
│ Use cases: │
│ - Static assets │
│ - User uploads │
│ - Backups │
│ - Large files │
│ │
└─────────────────────────────────────────────────────────────┘
R2 vs Supabase Storage
Comparison
| Feature |
R2 |
Supabase Storage |
| Egress fees |
Zero |
Included in plan |
| Max file |
5GB (single), unlimited (multipart) |
5GB |
| RLS integration |
No |
Yes |
| Image transforms |
No |
Yes |
| S3 compatible |
Yes |
Yes |
| Edge access |
Native |
Via API |
Khi nào dùng gì?
Use SUPABASE STORAGE when:
✅ Need RLS (user-specific access)
✅ Need image transformations
✅ Files tied to database records
✅ Simple setup preferred
Use R2 when:
✅ Large files (>50MB)
✅ Public static assets
✅ High egress volume
✅ Need S3 compatibility
✅ Edge Worker access
Basic Setup
Create Bucket
# Create R2 bucket
wrangler r2 bucket create my-bucket
# List buckets
wrangler r2 bucket list
Configuration
# wrangler.toml
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
Basic Operations
Worker Binding
interface Env {
MY_BUCKET: R2Bucket;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const key = url.pathname.slice(1); // Remove leading /
switch (request.method) {
case 'GET':
// Get object
const object = await env.MY_BUCKET.get(key);
if (!object) {
return new Response('Not Found', { status: 404 });
}
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
},
});
case 'PUT':
// Upload object
await env.MY_BUCKET.put(key, request.body, {
httpMetadata: {
contentType: request.headers.get('Content-Type') || 'application/octet-stream',
},
});
return new Response('Uploaded');
case 'DELETE':
// Delete object
await env.MY_BUCKET.delete(key);
return new Response('Deleted');
default:
return new Response('Method Not Allowed', { status: 405 });
}
},
};
Pricing
R2 Pricing
┌─────────────────────────────────────────────────────────────┐
│ R2 PRICING │
├─────────────────────────────────────────────────────────────┤
│ │
│ Free tier: │
│ - 10 GB storage │
│ - 1M Class A ops/month (PUT, POST, LIST) │
│ - 10M Class B ops/month (GET, HEAD) │
│ │
│ Paid: │
│ - $0.015/GB storage │
│ - $4.50/M Class A ops │
│ - $0.36/M Class B ops │
│ - $0 egress (always free!) │
│ │
│ Compared to S3: │
│ - S3: $0.09/GB egress │
│ - R2: $0 egress │
│ │
└─────────────────────────────────────────────────────────────┘
When to Consider R2
Good Fit
✅ High-traffic public assets
✅ CDN replacement
✅ S3 migration (API compatible)
✅ Large file storage
✅ Backup/archive storage
Not Ideal For
❌ User uploads needing RLS
→ Use Supabase Storage
❌ Image transformations
→ Use Supabase Storage
❌ Simple file needs
→ Supabase easier to set up
Tổng kết
Key Points
- S3-compatible API
- Zero egress fees
- Worker native binding
- Good for static assets
Recommendation for Light Stack
Primary: Supabase Storage
- User uploads
- RLS protection
- Image transforms
Secondary: R2 (optional)
- Very large files
- High-traffic public assets
- Cost optimization for egress
Q&A
- Có use case cần R2 không?
- File sizes thường bao nhiêu?
- Public hay private storage?