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.
npx flare create shop --storage r2On 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=r2The 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.
On the Next.js stack, four values
Section titled “On the Next.js stack, four values”Next.js has no bindings, so it talks to R2 over the S3 API and needs credentials.
Locally: nothing to sign up for
Section titled “Locally: nothing to sign up for”A new app’s docker compose up -d starts MinIO, which speaks S3, and .env
already points at it:
STORAGE_DRIVER=r2R2_ENDPOINT=http://127.0.0.1:9000R2_BUCKET=app-uploadsR2_ACCESS_KEY_ID=flareR2_SECRET_ACCESS_KEY=flare-secret-123Its 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”- 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.
- R2 → Create bucket. Any name; leave the location automatic. The name is
your
R2_BUCKET. - 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.
- 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 |
Setting them where you deploy
Section titled “Setting them where you deploy”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):
npx wrangler secret put R2_ACCESS_KEY_IDnpx wrangler secret put R2_SECRET_ACCESS_KEYSecrets, 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.
Any other S3 will do
Section titled “Any other S3 will do”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.
Further reading
Section titled “Further reading”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.