Skip to main content
R2 uses the S3 API with AWS Signature V4 against https://<account-id>.r2.cloudflarestorage.com. MIRAGE derives this endpoint from your account ID automatically. Credentials (R2 API token with object read/write scopes) are created the same way in both runtimes, see R2 Credentials.

Node (server-side)

Browser (presigned URLs)

The browser R2Resource is secret-free, your backend signs each operation using your R2 keys and returns a URL. R2 accepts AWS Signature V4, so @aws-sdk/s3-request-presigner works with region: 'auto' pointed at R2’s endpoint.

1. Server: sign URLs with the R2 endpoint

2. Browser: wire it up

accountId on the browser config is only used for display/logging; the actual endpoint is baked into the presigned URLs your backend returns.

3. Configure CORS on the bucket

R2 does accept S3-flavored PutBucketCors, but your token needs “Admin Read & Write” permissions on the bucket, object-scoped tokens will get Access Denied. Two paths: Option A, Cloudflare Dashboard (fastest, no new token):
  1. Open https://dash.cloudflare.comR2 → your bucket
  2. Settings tab → CORS PolicyAdd CORS policy
  3. Origin: http://localhost:5173 (and any production origins), methods GET,PUT,HEAD,DELETE,POST, allowed headers *
Option B, admin-scoped R2 API token + the helper script:
  1. Cloudflare Dashboard → R2Manage R2 API TokensCreate API token
  2. Permissions: Admin Read & Write on your bucket
  3. Update R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY in .env.development
  4. Run the same helper used for S3:
Object-scoped tokens (Object Read & Write) cannot edit CORS. If the script returns Access Denied on R2, you’re almost certainly using an object token.
See the R2 resource docs for the equivalent Python wiring.