Skip to content

Cloudflare R2

S3-compatible object storage with no charge for bandwidth out. For an app that serves files, egress is usually the biggest line on the bill, which is why this is the default on both stacks.

Terminal window
npx flare create shop --storage r2

On the Cloudflare stack, there is nothing to do

Section titled “On the Cloudflare stack, there is nothing to do”

Your Worker is bound to a bucket. Wrangler creates it on your first deploy and simulates it locally as a directory, so there is no endpoint, no access key and nothing to paste:

STORAGE_DRIVER=r2

The binding is in wrangler.jsonc as STORAGE. Skip the rest of this page unless you want to point a Cloudflare app at a bucket in a different account.

Next.js has no bindings, so it talks to R2 over the S3 API and needs credentials.

A new app’s docker compose up -d starts MinIO, which speaks S3, and .env already points at it:

STORAGE_DRIVER=r2
R2_ENDPOINT=http://127.0.0.1:9000
R2_BUCKET=app-uploads
R2_ACCESS_KEY_ID=flare
R2_SECRET_ACCESS_KEY=flare-secret-123

Its console is on http://127.0.0.1:9001 (same user and password) and shows what was uploaded. This is the same code path you deploy — only the four values change.

For production: making a bucket and a token

Section titled “For production: making a bucket and a token”
  1. Sign up at dash.cloudflare.com and add a payment method. R2 has a free tier of 10 GB but will not enable without a card on file — this is the step that surprises people.
  2. R2 → Create bucket. Any name; leave the location automatic. The name is your R2_BUCKET.
  3. R2 → Manage R2 API tokens → Create API token. Give it a name and Object Read & Write on that bucket. Admin-level works but grants more than an app needs.
  4. Copy the three values it shows you once. It will not show the secret again.
Flare needs Where it comes from
R2_ENDPOINT Shown with the token as the S3 endpoint: https://<account-id>.r2.cloudflarestorage.com
R2_BUCKET The bucket name from step 2
R2_ACCESS_KEY_ID Shown once, when the token is created
R2_SECRET_ACCESS_KEY Shown once, when the token is created

On Vercel: Project → Settings → Environment Variables, one per value. Then redeploy — environment variables are read at build time.

On Cloudflare (pointing a Worker at an external bucket rather than using its binding):

Terminal window
npx wrangler secret put R2_ACCESS_KEY_ID
npx wrangler secret put R2_SECRET_ACCESS_KEY

Secrets, not wrangler.jsonc vars — anything in that file is committed.

Flare uploads go through your app, not from the browser straight to R2, so you do not need a CORS rule. If you later upload direct from the browser, add one in the bucket’s settings listing your origins and GET, PUT, POST, DELETE.

The driver is plain S3, so these four values can point at MinIO, Backblaze B2, DigitalOcean Spaces or AWS S3. For AWS, R2_ENDPOINT is https://s3.<region>.amazonaws.com.

The ultimate guide to file uploads with Cloudflare R2 and AWS S3 walks through the dashboard screens for both, with the CORS and IAM steps in full.