Cloudflare R2 — Next Steps & External Setup

Everything you need to do outside the codebase to enable Cloudflare R2 file storage.


1. Rails Credentials

Run bin/rails credentials:edit and add:

cloudflare:
  r2_account_id: "your-cloudflare-account-id"
  r2_access_key_id: "your-r2-access-key-id"
  r2_secret_access_key: "your-r2-secret-access-key"
  r2_bucket: "your-bucket-name"

2. Create an R2 Bucket

  1. Go to Cloudflare Dashboard
  2. Select your account
  3. Go to R2 Object Storage in the sidebar
  4. Click Create bucket
  5. Name: Choose a name (e.g., yourapp-production)
  6. Location: Choose a location hint (auto is fine)

3. Enable Public Access (Custom Domain)

  1. In your bucket settings, go to Settings → Public access
  2. Under Custom Domains, click Connect Domain
  3. Enter a subdomain: cdn.yourdomain.com (or assets.yourdomain.com)
  4. Cloudflare will automatically create a DNS record
  5. Wait for the SSL certificate to be issued

Why custom domain? The storage.yml is configured with public: true, which means ActiveStorage will generate direct public URLs to your R2 bucket. A custom domain gives you clean URLs like https://cdn.yourdomain.com/... instead of the raw R2 URLs.


4. Create API Tokens

  1. Go to R2 Object Storage → Manage R2 API Tokens
  2. Click Create API Token
  3. Token name: yourapp-production
  4. Permissions: Object Read & Write
  5. Specify bucket: Select your bucket (or "Apply to all buckets")
  6. Click Create API Token
  7. Copy:
    • Access Key IDr2_access_key_id
    • Secret Access Keyr2_secret_access_key

5. Get Account ID

  1. In the Cloudflare Dashboard sidebar, your Account ID is shown under the account name
  2. Or go to any zone → Overview → right sidebar shows Account ID
  3. Copy this to r2_account_id

6. CORS Configuration (if needed)

If you get CORS errors when loading images directly from R2:

  1. Go to your bucket → Settings → CORS Policy
  2. Add a rule:
    [
      {
        "AllowedOrigins": ["https://yourdomain.com"],
        "AllowedMethods": ["GET", "HEAD"],
        "AllowedHeaders": ["*"],
        "MaxAgeSeconds": 86400
      }
    ]
    

7. Deployment Checklist

  • R2 bucket created (for file uploads)
  • Custom domain connected (for public URLs)
  • API token created with Read & Write permissions
  • All 4 Cloudflare credentials added (r2_account_id, r2_access_key_id, r2_secret_access_key, r2_bucket)
  • CORS configured if needed
  • Upload an image (e.g., project logo) — verify it's stored in R2 and publicly accessible
  • Images load from cdn.yourdomain.com in browser
  • Image variants (thumbnails) generate correctly