# Cloudflare R2 (https://v2.s3.dimah.dev/docs/providers/cloudflare-r2)

Cloudflare R2 supports the S3 API with two important differences: it has no
presigned POST, and object ACLs are ignored.

## 1. Server configuration [#1-server-configuration]

Point the `S3Client` at your account's R2 endpoint. Uploads use presigned PUT
by default, which is what R2 needs:

```ts title="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", // [!code ++]
});

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.

## 2. Bucket CORS policy [#2-bucket-cors-policy]

Add a CORS rule in the Cloudflare R2 dashboard for your frontend domain:

```json
[
  {
    "AllowedOrigins": ["https://your-app.example"],
    "AllowedMethods": ["GET", "PUT", "HEAD"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag", "Content-Type"],
    "MaxAgeSeconds": 3000
  }
]
```

## Frequently asked questions [#frequently-asked-questions]

<Accordions>

<Accordion title="How do I serve R2 files publicly?">

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`](https://v2.s3.dimah.dev/docs/core#helpers)
handles URL encoding for you.

</Accordion>

</Accordions>
