Providers
Cloudflare R2
Endpoint configuration, CORS, and public URLs for Cloudflare R2.
Cloudflare R2 supports the S3 API with two important differences: it has no presigned POST, and object ACLs are ignored.
Server configuration
Point the S3Client at your account's R2 endpoint. Uploads use presigned PUT
by default, which is what R2 needs:
import { S3Client } from "@aws-sdk/client-s3";
import { dimahS3, route } from "@dimah-s3/server";
export const awsS3 = 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!,
},
requestChecksumCalculation: "WHEN_REQUIRED",
});
export const s3 = dimahS3({
client: awsS3,
bucket: process.env.R2_BUCKET!,
routes: {
avatar: route({
upload: {
fileTypes: ["image/*"],
maxFileSize: 2 * 1024 * 1024,
},
}),
},
});Do not set upload.method: "POST" on R2 routes. The presign succeeds, but R2
rejects the form upload.
Bucket CORS policy
Add a CORS rule in the Cloudflare R2 dashboard for your frontend domain:
[
{
"AllowedOrigins": ["https://your-app.example"],
"AllowedMethods": ["GET", "PUT", "HEAD"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag", "Content-Type"],
"MaxAgeSeconds": 3000
}
]Frequently asked questions
Enable public access for the bucket or connect a custom domain such as
cdn.example.com. Then build URLs from that public base URL and the
server-issued object key; buildPublicObjectUrl
handles URL encoding for you.