Kirill Platonov
Kirill Platonov
All Posts Jan 8, 2023

Setting up ActiveStorage with Cloudflare R2

Cloudflare R2 is a new object storage that has been launched in 2022. It offers S3-compatible storage with built-in CDN. The pricing is much simpler and affordable compared to the combination of AWS S3 + Cloudfront which are commonly used for the same goal. Another benefit is that you don’t have to pay for data transfer between AWS regions.

While savings on data transfer fees are noticeable only on large scale I think Cloudflare R2 is a great option for new projects too:

  • You don’t have to set up and maintain separate CDN for your object storage (eg Cloudfront or Fastly).
  • R2 offers a free plan with 10 GB storage and 10m read requests. Going beyond these limits is still much cheaper than S3 + Cloudfront.
  • R2 setup is much simpler than S3 + Cloudfront which could save you some valuable time in the begging.

If you decided to try Cloudflare R2 for your project then the next question is how to make it work with your framework. In my case, it’s Rails and ActiveStorage. Since R2 is S3-compatible it should be pretty easy to make it work with ActiveStorage. And while it’s mostly true I still wanted to document the process to make it easily repeatable.

Step 1. Setup ActiveStorage

Skip this step if you already installed Active Storage.

Install ActiveStorage:

bin/rails active_storage:install
bin/rails db:migrate

Add the following gems to Gemfile:

gem "image_processing", "~> 1.2"
gem "aws-sdk-s3", require: false

And run bundle install.

Step 2. Create R2 bucket

Create Cloudflare R2 bucket: Cloudflare R2 bucket creation process

Generate S3 Auth token: Cloudflare R2 auth token generation step 1 Cloudflare R2 auth token generation step 2 Cloudflare R2 auth token generation step 3 Cloudflare R2 auth token generation step 4

Copy Access Key ID and Access Key. They will be used in next step. Cloudflare R2 credentials

Also, copy Account ID. It will be required in the next step: Cloudflare R2 account ID

Step 3. Configure ActiveStorage

Add Cloudflare to ActiveStorage.

Use bin/rails credentials:edit to set Cloudflare credentials:

cloudflare:
  account_id: ACCOUNT_ID
  access_key_id: YOUR_KEY
  secret_access_key: YOUR_SECRET
  bucket: BUCKET_NAME

Now update config/storage.yml:

cloudflare:
  service: S3
  endpoint: https://<%= Rails.application.credentials.dig(:cloudflare, :account_id) %>.r2.cloudflarestorage.com
  access_key_id: <%= Rails.application.credentials.dig(:cloudflare, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:cloudflare, :secret_access_key) %>
  region: auto
  bucket: <%= Rails.application.credentials.dig(:cloudflare, :bucket) %>

And update ActiveStorage service in config/environments/development.rb:

config.active_storage.service = :cloudflare

Step 4. Setup CORS (optional)

This step is necessary only if you plan to use ActiveStorage direct upload. Unfortunately, Cloudflare R2 doesn’t have a UI for managing bucket CORS configuration. However it cloud be easily done via S3 API and aws-sdk-s3 gem. I use the following code to set CORS (you’ll need to run it in Rails console):

bucket = ActiveStorage::Blob.service.bucket
bucket.cors.put(cors_configuration: {
  cors_rules: [
    {
      allowed_headers: ["*"],
      allowed_methods: ["PUT"],
      allowed_origins: ["*"], # replace with app host with https for production
      expose_headers: ["Origin", "Content-Type", "Content-MD5", "Content-Disposition"],
      max_age_seconds: 3600
    }
  ]
})

Pay attention to allowed_origins attribute. You can use wildcard (*) for development. But for production, you’ll need to replace it with an app host with protocol.

UPD April 2023:

Cloudflared launched beta version of UI for managing buckets CORS configuration. You can use it instead of the code above.

Open bucket settings: Cloudflare R2 bucket

Click on “Add CORS policy”: Cloudflare R2 bucket settings

Enter the following policy. It’s an equivalent of the code above:

[
  {
    "AllowedOrigins": [
      "*"
    ],
    "AllowedMethods": [
      "PUT"
    ],
    "AllowedHeaders": [
      "*"
    ],
    "ExposeHeaders": [
      "Origin",
      "Content-Type",
      "Content-MD5",
      "Content-Disposition"
    ],
    "MaxAgeSeconds": 3600
  }
]

Cloudflare R2 CORS policy

That’s it. Your Cloudflare R2 bucket is ready to be used with ActiveStorage. You’ll need to repeat the process for production/staging buckets. The process is really straightforward but it took me a while to figure out how to set up CORS properly. I hope this guide will help you to save some time.

Useful links:

If want to hear more from me follow me on Twitter. I tweet about my journey of building and growing Shopify apps and about Ruby on Rails development.

Subscribe to get new blog posts via email

Or grab the RSS feed