What is Cloudflare R2?

Cloudflare R2 ‘…allows developers to store large amounts of unstructured data without the costly egress bandwidth fees associated with typical cloud storage services’. It is essentially the same service as Amazon’s S3 storage buckets, but with the key selling points of having a really generous free tier and being overall cheaper than S3 according to their own costs calculator.

All of the large-scale projects I’ve worked on in my career have utilised storage buckets such as these to handle media files. This adds a whole new layer specifically for storing these bulky files such as images, documents and videos, offloading the work from the main server and keeping them out of the Git repo where they don’t belong.

Not only is Cloudflare R2 similar to Amazon S3, but it’s also set up to be almost entirely compatible as a drop-in-replacement. This is a huge plus for any existing Amazon S3 users that are interested in swapping due to pricing or platform preferences, as you can theoretically import the contents from your old Amazon S3 bucket to a new Cloudflare R2 one and be ready to go almost immediately.

Does Cloudflare R2 work with WordPress

Integrating Cloudflare R2 with WordPress is a little bit awkward if, like me, you’d prefer not to be paying monthly for an ‘out of the box’ integration. Additionally, most media offload plugins out there are, in my opinion, quite bloated. I don’t personally feel a need for admin menus, scripts, and over-developed customisation options for what should only require some minor tweaks during setup before being left alone.

To answer the burning question that I’ve seen thrown around in forums: Yes, you absolutely can use Cloudflare R2 with WordPress entirely for free- every single piece of media on my own website does so! You can do this with almost no bloat, and get blazingly fast media as a result- for free.

Humanmade’s S3 Uploads plugin Github repo

Enter Humanmade’s S3 Uploads. Now, as the name suggests, this plugin only officially supports S3 but, as we’ve already covered, Cloudflare R2 is set up to be almost entirely compatible as a drop-in-replacement. While the S3 Uploads plugin devs have noted that they don’t currently plan to offer ‘official’ support, users have been able to use S3 Uploads with Cloudflare R2 without doing anything beyond the standard process of providing the required credentials, and then changing one extra setting (explained in detail later).

Setting up Cloudflare R2 API keys for WordPress

For the sake of this tutorial, I’m not going to explain how to set up a Cloudflare R2 bucket and will assume you already have one set up and ready for use. Once your Cloudflare R2 bucket is set up and publicly available, you’ll also need to create some API credentials.

To create the API access keys you should:

  • On the sidebar in Cloudflare click on ‘R2’ > ‘Overview’.
  • On the top-right, click ‘Manage R2 API Tokens’.
  • Next, click ‘Create API Token’.
  • Name your API token, select the appropriate bucket, and set the expiry date.
  • For the Permissions, set it to ‘Object Read & Write’.
  • Finally, save the Access Key ID and the Secret Access Key which are generated.

Note: The Access Key ID and Secret Access Key should be stored somewhere securely. If you don’t set an expiry on these keys, then it’s important to ensure nobody can access them as it’s a potential security risk to anything stored on your bucket.

Use Cloudflare R2 with S3 Uploads

To get started, first download and install S3 Uploads into your WordPress website. Unlike most plugins you find for WordPress, S3 Uploads does its work from the shadows and offers no GUI-based settings configurations in the WordPress admin menus. I like this as it keeps things clean and minimal, but it also makes sense as, once the WordPress admin screens are loaded, we’re already passed the point where the image server configurations need to be handled. Instead of the admin menu, we’ll be making our configuration changes within our wp-config.php file.

Note: In my testing, I wasn’t able to get any success while modifying these settings within a standard custom plugin or a theme’s functions.php file as this is also a little bit too late in the request lifecycle in WordPress. You can set this up in an mu-plugin as these are loaded in much sooner than the options I’ve just mentioned, and that’s how I personally handle this on my own websites.

As per this discussion on the Github repo, you can connect to your storage bucket by adding the following to your wp-config.php file:

define("S3_UPLOADS_ENDPOINT", "https://<Cloudflare account ID>.r2.cloudflarestorage.com")
define("S3_UPLOADS_BUCKET", "<R2 bucket Name>")
define("S3_UPLOADS_BUCKET_URL", "<R2 bucket public url or domain>")
define("S3_UPLOADS_REGION", "auto")
define("S3_UPLOADS_KEY", "<R2 access key ID>")
define("S3_UPLOADS_SECRET", "<R2 secret access key>")

This is produced based on an example found within the R2 docs.

On its own, this isn’t quite enough to get everything working, as we also need to utilise a hook to override the default URL to be the custom endpoint we’ve set instead of Amazon’s S3. This will need to be added to either your theme’s functions.php file, or a custom plugin.

function tw_s3_uploads_s3_client_params( $params ) {
    $params["endpoint"] = S3_UPLOADS_ENDPOINT;
    $params["use_path_style_endpoint"] = true;
    return $params;
}
add_filter( "s3_uploads_s3_client_params", "tw_s3_uploads_s3_client_params");

Note: The above code snippets are copied directly from the Github discussion linked here. Both code snippets were originally written by Github user tedyw and, while I use modified versions of these for my own deployments, I’ve left the snippets unchanged from his original versions so he can be appropriately credited for his investigations and work on this topic.

Testing that media is offloading to Cloudflare R2

Once you’ve added the above code snippets to the correct locations and have entered the appropriate information, it’s time to test everything.

Log in to your WordPress dashboard, go to the media library, and try uploading a file: preferably a small image. It should display and behave as normal, with the only difference being that its URL will clearly point towards your storage bucket.

Additionally, if you go to your R2 bucket within Cloudflare, you’ll also be able to navigate the files stored on it. You should be able to see all of your uploads on there.

Note: while you can fill the bucket with lots of media files, WordPress will only be ‘aware’ of the media if there is the appropriate data within it’s database. This means that if you connect a single storage bucket to two sites, they would both use it in parallel without seeing eachother’s media. This is not a recommended way to use buckets, you should always use seperate buckets for seperate projects, but it’s important to note this behaviour if, like me, you often swap between local and production environments.

Troubleshooting

If you’re having issues with uploading media after setting the above code snippets, there are a few things you can do to troubleshoot. This isn’t an exclusive list of things to try, but might help you if you’re unsure on where to begin:

  • Check the API credentials you’ve provided are correct and that you’ve added them to the code appropriately as per the examples.
  • Check your developer console logs for any indication of what might be going wrong. For example, I had some CORS issues which were a result of the two different URLs trying to work together. To resolve this, I had to change the bucket settings to allow the website’s domain.
  • If you’re encountering PHP errors as a result of adding the code then enable WP_DEBUG. This should indicate what’s going wrong, so you can proceed in accordance with what you see there.