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:
Generate S3 Auth token:
Copy Access Key ID and Access Key. They will be used in next step.
Also, copy Account ID. It will be required in the next step:
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:
Click on “Add CORS policy”:
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
}
]
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.