Integrating Cloudflare R2 Storage with Laravel

lokman musliu
Lokman Musliu

January 1, 2024 · 5 min read · 1,934 views

integrating cloudflare r2 with laravel

Are you on the hunt for a more affordable and efficient object storage solution than AWS S3? Look no further than Cloudflare R2, an S3-compatible storage service that boasts zero egress fees and a generous free tier, making it a wallet-friendly choice for developers and businesses alike. In this comprehensive guide, I'll take you through the process of integrating Cloudflare R2 with your Laravel application, ensuring a smooth transition and significant cost reductions. From installation to configuration, and even setting up public access, every step is covered to help you leverage Cloudflare R2's full potential with Laravel.

Cloudflare R2 vs AWS S3

When comparing Cloudflare R2 and Amazon S3 for object storage, Cloudflare R2 offers a compelling free tier with 10 GB of storage and substantial operation allowances, as opposed to S3's 5 GB and fewer operations. R2's absence of egress fees can result in significant cost savings, especially for high data retrieval needs, while it strives to compete with S3's global reliability and speed.

Cloudflare R2 not only provides an S3-compatible API and migration tool for easy switching but also boasts lower storage and operation costs post-free tier, positioning it as a cost-effective solution for high-volume data users. Meanwhile, Amazon S3 is a reliable contender, particularly for those already invested in the AWS ecosystem, but users should consider both financial and technical factors to choose the service that best aligns with their requirements.

Install the S3 Filesystem Driver

Since Cloudflare R2 offers a S3 Compatible API, we can seamlessly integrate the league/flysystem-aws-s3-v3 driver to work in tandem with it. We can install the driver with the following command:

composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies

Configure a New Disk

Next, you'll need to update your disk configuration. In your Laravel project, locate and edit the config/filesystems.php file to include a new r2 disk configuration as follows:


return [

    | Default Filesystem Disk
    | Here you may specify the default filesystem disk that should be used
    | by the framework. The "local" disk, as well as a variety of cloud
    | based disks are available to your application. Just store away!

    'default' => env('FILESYSTEM_DISK', 'local'),

    | Filesystem Disks
    | Here you may configure as many filesystem "disks" as you wish, and you
    | may even configure multiple disks of the same driver. Defaults have
    | been setup for each driver as an example of the required options.
    | Supported Drivers: "local", "ftp", "sftp", "s3"

    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',

        'r2' => [
             'driver' => 's3',
             'key' => env('CLOUDFLARE_R2_ACCESS_KEY_ID'),
             'secret' => env('CLOUDFLARE_R2_SECRET_ACCESS_KEY'),
             'region' => 'us-east-1', // Cloudflare R2 doesn't have specific regions, so 'us-east-1' is fine.
             'bucket' => env('CLOUDFLARE_R2_BUCKET'),
             'url' => env('CLOUDFLARE_R2_URL'),
             'visibility' => 'private',
             'endpoint' => env('CLOUDFLARE_R2_ENDPOINT'),
             'use_path_style_endpoint' => env('CLOUDFLARE_R2_USE_PATH_STYLE_ENDPOINT', false),
             'throw' => false,

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
            // 'visibility' => 'public', //

        'assets' => [
            'driver' => 'local',
            'root' => public_path('assets'),
            'url' => '/assets',
            'visibility' => 'public',

    | Symbolic Links
    | Here you may configure the symbolic links that will be created when the
    | `storage:link` Artisan command is executed. The array keys should be
    | the locations of the links and the values should be their targets.

    'links' => [
        public_path('storage') => storage_path('app/public'),


Let's go ahead and update our environment file with the necessary variables. Remember, it's always a good practice to keep the .env.example file in sync as well. By doing so, you'll be helping your fellow collaborators to set up their environments more efficiently.


Create a New R2 Bucket

Let's get started with setting up your storage bucket on Cloudflare! First, log in to your Cloudflare dashboard. Look for the "R2" section in the sidebar and click on it. Once you're there, you'll see an option to "Create bucket" – go ahead and select that. For this guide, we'll name our new bucket images.

Next up is deciding on the location for your bucket. If your application needs to comply with GDPR, we recommend using the Specify Jurisdiction option and selecting the European Union to ensure compliance. For everyone else, choosing Automatic will let Cloudflare intelligently serve your assets from the location nearest to your users, optimizing for speed and efficiency.

r2 create bucket screen

Generate an API Token

To begin, navigate to the R2 page within your Cloudflare dashboard and click on Manage R2 API Tokens. Here, you'll want to create a new token and grant it Object Read & Write permissions, ensuring that it is specifically associated with the images bucket we previously set up. It's important to avoid assigning full access to your API key, as this can lead to a security nightmare.

After you've successfully created the token, Cloudflare will provide you with an Access Key ID and a Secret Access Key. You must keep these credentials safe and secure, as they are the keys to accessing your Cloudflare resources programmatically. Treat them with the same level of security you would for your most confidential information.

cloudflare r2 create api token screen

Public Bucket

If you need to keep your bucket private, skip this step!

To make your files publicly accessible, you'll need to activate the Public Access feature for your bucket. The good news is that if you're using Cloudflare as your DNS provider, this process is simplified for you. All you need to do is specify the domain you wish to use. In our case, we've chosen as our domain.

Once you've linked a custom domain to your storage bucket, anyone can access the contents of your bucket through that domain. Moreover, your application can reap the advantages of Cloudflare, including bot management, the Access service, and enhanced caching capabilities.

Cloudflare R2 CORS Policy

When you attempt to upload files using the Storage facade, you might encounter a CORS error. This happens because CORS policies require proper configuration to allow resource sharing between different origins. To ensure a smooth integration, it's essential to set up CORS correctly. Here's how you can do it step by step:

Head over to your bucket, go to the CORS Policy and click on the Add CORS Policy button.

You can add two origins ( local and production ):

The .test domain is our local Valet domain that we use for testing.

    "AllowedOrigins": [
    "AllowedMethods": [

Update Your Environment File

With the credentials that were shown to you in the previous step, we now need to update our env file as shown below:


Using Cloudflare R2 with Laravel Storage Facade

All set to go! You can now seamlessly integrate your R2 storage with the Laravel Storage facade. Here's a small example from the Laravel documentation:

use Illuminate\Support\Facades\Storage;
Storage::disk('r2')->put('example.txt', 'Contents');

For added convenience, you can set r2 as your default disk in your .env file, eliminating the need to specify disk('r2') each time:


There you have it—a step-by-step guide to integrating Cloudflare R2 with your Laravel application. Enjoy the savings and the seamless experience!

Bring Your Ideas to Life 🚀

If you need help with a Laravel project let's get in touch.

Lucky Media is proud to be recognized as a Top Laravel Development Agency

lokman musliu
Lokman Musliu

Founder and CEO of Lucky Media


Heading Pattern

Related Posts

Stay up to date

Be updated with all news, products and tips we share!