Storage
Cloudflare R2
Setting up Cloudflare R2 as your storage provider.
Setup
- Create a Cloudflare account and enable R2 in the dashboard
- Create an R2 bucket
- Enable public access for the bucket (or set up a custom domain)
- Create an R2 API token with Object Read & Write permissions
- Set environment variables:
NEXT_PUBLIC_STORAGE_SERVICE="r2"
R2_ACCESS_KEY_ID=""
R2_SECRET_ACCESS_KEY=""
R2_ENDPOINT="https://<account-id>.r2.cloudflarestorage.com"
R2_BUCKET="your-bucket-name"
R2_PUBLIC_URL="https://pub-xxx.r2.dev"How it works
R2 uses the S3-compatible API via @aws-sdk/client-s3:
function createR2Provider(): StorageProvider {
return {
async upload(file, key, contentType) {
const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3");
const client = new S3Client({
region: "auto",
endpoint: process.env.R2_ENDPOINT!,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
});
await client.send(
new PutObjectCommand({
Bucket: process.env.R2_BUCKET,
Key: key,
Body: file,
ContentType: contentType,
})
);
return {
url: `${process.env.R2_PUBLIC_URL}/${key}`,
key,
};
},
async delete(key) {
const { S3Client, DeleteObjectCommand } = await import("@aws-sdk/client-s3");
// ... similar setup, sends DeleteObjectCommand
},
};
}The S3 SDK is dynamically imported so it's not bundled when Supabase Storage is used.
URL format
Uploaded files are accessible at:
{R2_PUBLIC_URL}/{key}For example: https://pub-xxx.r2.dev/avatars/user-123.jpg
Getting R2 credentials
- Go to Cloudflare Dashboard > R2
- Click Manage R2 API Tokens
- Create a token with:
- Permissions: Object Read & Write
- Scope: Your specific bucket
- Copy the Access Key ID and Secret Access Key
- Your endpoint is:
https://<account-id>.r2.cloudflarestorage.com