2.0 beta — new releases can include breaking changes. Stable 1.x docs
dimah-s3v2.0.0-beta.0
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:

lib/s3.ts
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

On this page