DEV Community

Cover image for Cloudflare R2 Hands-On Guide: Set Up Free 10GB Storage, Zero-Egress Object Storage, and S3-Compatible API Keys
Emily Johnson
Emily Johnson

Posted on

Cloudflare R2 Hands-On Guide: Set Up Free 10GB Storage, Zero-Egress Object Storage, and S3-Compatible API Keys

In a modern full-stack (or vibe coding) stack, even though Supabase ships with Storage and Vercel offers free Blob quotas, many developers (including HiCyou) still prefer Cloudflare R2 as the primary storage layer for SaaS projects.

The main reasons:

  • R2 is fully compatible with the S3 API.
  • Most importantly, it offers zero egress fees. That means no matter how many times users download images or files from your app, you don’t get hit with surprise bandwidth bills. (There are request charges, but they’re very low in practice.) This is huge for SaaS scalability.

For early-stage indie founders, R2’s free tier is extremely generous: 10GB of free storage, free egress, and plenty of free requests, which is ideal for launching projects at basically zero infrastructure cost.

In this guide, I’ll walk you through:

  1. Creating an R2 bucket
  2. Generating S3-compatible API credentials
  3. Configuring a custom domain for public access

Step 1: Create Your R2 Bucket

img

Before you begin, make sure you have a Cloudflare account.

  1. Log in to the Cloudflare Dashboard.
  2. In the sidebar, find and click R2.
    • Note: If this is your first time using R2, Cloudflare may ask you to add a payment method. R2 still offers a very generous free tier (10GB storage + millions of requests per month), but a credit card or PayPal is usually required to activate the service.
  3. Click “Create bucket”.

Configure the Bucket

img

  • Bucket Name: Enter a unique name (for example, hicyou-assets or my-saas-uploads).
    • Tip: Only lowercase letters, numbers, and hyphens are allowed.
  • Location: You can leave this as Automatic. Cloudflare’s global network will handle edge routing and optimization for you.
  • Click “Create Bucket”.

You now have an R2 bucket. At this point, it’s still private – your code and your users can’t access it yet.


Step 2: Generate API Keys (S3 Credentials)

To let your application (Node.js, Python, Go, etc.) upload and read files, you need credentials. Since R2 is S3-compatible, you’ll need an Access Key ID and Secret Access Key.

Important: Don’t look for these inside the bucket itself. They’re configured in the R2 account-level settings, not under a specific bucket.

img

  1. Go back to the R2 Overview page.
  2. On the right sidebar, under “Account Details”, click the {} Manage button.
  3. Click “Create User API Token”.

Permission Wizard

To make sure your app can read and write objects correctly, configure the token like this:

img

  • Token Name: Use a descriptive name (for example, Production App Read/Write).
  • Permissions: Select Object Read & Write.
    • Security tip: For app credentials, avoid using full Admin permissions. Object Read & Write is safer and typically all you need.
  • Specific Bucket(s): Choose “Apply to specific buckets only” and select the bucket you just created (for example, hicyou-assets).
  • TTL: Choose “Forever” (unless you already have a key rotation policy in place).

Click “Create User API Token”.

⚠️ Important: Save Your Keys Immediately

Cloudflare will only show you the Secret Key once. If you close the page, you won’t be able to view it again.

Copy and store the following values in your .env file or password manager:

  1. Access Key ID (e.g. f82b...)
  2. Secret Access Key (e.g. 8d9a... – a long random string)
  3. Endpoint – your S3-compatible endpoint, typically something like:https://.r2.cloudflarestorage.com

Developer tip: When configuring an S3 client (AWS SDK, MinIO, etc.), the Endpoint usually does not include the bucket name. The client will add the bucket to the URL automatically when making requests.


Step 3: Enable Public Access (Custom Domain)

By default, objects in R2 are private. If you want users to view images (avatars, blog cover images, etc.) directly in their browser, you need to enable some form of public access.

Best Practice: Use Your Own Custom Domain

Avoid using the default *.r2.dev domain in production – it has strict rate limits. Instead, bind a domain you control.

img

  1. Open your Bucket and go to the Settings tab.
  2. Scroll down to Public AccessCustom Domains.
  3. Click “Connect Domain”.
  4. Enter a subdomain such as cdn.hicyou.com or assets.myapp.com.
  5. Cloudflare will automatically create and configure the necessary DNS records for you.

Now, if you upload a file named logo.png, users can access it via:

https://assets.hicyou.com/logo.png
Enter fullscreen mode Exit fullscreen mode

Step 4: Configure CORS (For Direct Uploads from the Frontend)

If you want your frontend (React, Next.js, Vue, etc.) to upload directly to R2 instead of proxying through your backend, you must configure CORS (Cross-Origin Resource Sharing) for your bucket.

  1. In your Bucket Settings page.
  2. Scroll down to CORS Policy.
  3. Add JSON similar to the following to allow both local development and your production domains:
[
  {
    "AllowedOrigins": [
      "http://localhost:3000",
      "https://hicyou.com",
      "https://www.hicyou.com"
    ],
    "AllowedMethods": [
      "GET",
      "PUT",
      "POST",
      "DELETE",
      "HEAD"
    ],
    "AllowedHeaders": [
      "*"
    ],
    "ExposeHeaders": [],
    "MaxAgeSeconds": 3000
  }
]
Enter fullscreen mode Exit fullscreen mode

Conclusion

Combined with the previous Supabase guide, you now have two core pieces of infrastructure ready for your SaaS:

  • Postgres (via Supabase) for your database
  • Cloudflare R2 for object storage

Both are battle-tested, highly cost-effective options for indie devs and small teams.

You’re welcome to use https://github.com/hicyoucom/hicyou to spin up your own AI-powered link directory site.

Demo: https://hicyou.com

Top comments (0)