How to Store and Serve Files in Laravel Using Cloudflare R2

If you’re running a Laravel project and need affordable object storage, Cloudflare R2 is a great option. It works similarly to AWS S3 but without egress fees, which makes it ideal for projects that need to serve a lot of data. The only catch? Laravel doesn’t support R2 out of the box, so you’ll need to configure it manually.
This guide walks you through setting up Cloudflare R2 with Laravel. We’ll cover everything from creating an R2 bucket to configuring Laravel so you can store and retrieve files with ease.
Step 1: Set Up Cloudflare R2
Before Laravel can store files in Cloudflare R2, you need an R2 bucket and API credentials. Here’s how to get started:
- Log into Cloudflare and navigate to the R2 section.
- Create a new bucket and give it a unique name.
- Generate API credentials:
- Go to ‘API Tokens’ in your Cloudflare dashboard.
- Create a new token with permissions to read and write to R2.
- Copy the Access Key ID and Secret Access Key somewhere safe. You’ll need them in Laravel.
Step 2: Install AWS SDK for Laravel
Cloudflare R2 is S3-compatible, so Laravel can interact with it using AWS’s SDK. Install the necessary package via Composer:
composer require league/flysystem-aws-s3-v3Step 3: Configure Laravel for Cloudflare R2
Laravel’s filesystem configuration is in config/filesystems.php. Since Cloudflare R2 is S3-compatible, you can use the existing S3 driver, but with some modifications. Open the file and add a new disk configuration for R2:
'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),
'throw' => false,
],Then, in your .env file, add the credentials you saved earlier:
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_BUCKET=your-bucket-name
AWS_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
AWS_USE_PATH_STYLE_ENDPOINT=false
AWS_DEFAULT_REGION=us-east-1Key Fixes & Explanations
- Region Setting: R2 doesn’t require regions like AWS S3, but Laravel expects one. Leaving it as an empty string (
'') prevents errors. - Endpoint Format: Ensure your endpoint follows this format:
https://<your-account-id>.r2.cloudflarestorage.comNow, Laravel is ready to store and retrieve files using Cloudflare R2.
Step 4: Test File Uploads
Now that Laravel is set up, you can test file uploads. Create a route to upload a file:
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\Request; Route::post('/upload', function (Request $request) {
$path = $request->file('file')->store('uploads', 's3');
return response()->json(['path' => $path]);
});
Try uploading a file through Postman or a simple HTML form. If everything is set up correctly, Laravel should store the file in Cloudflare R2.
Step 5: Serving Files from Cloudflare R2
If you want to make files publicly accessible, you’ll need to generate URLs. Use Laravel’s Storage facade like this:
$url = Storage::disk('s3')->url('uploads/example.jpg');Since Cloudflare R2 doesn’t support direct public file access like S3, you may need to set up a Cloudflare Worker to serve files securely.
Cloudflare R2 Limitation & Workaround
Unlike AWS S3, Cloudflare R2 does not support setting different access levels within the same bucket. This means you cannot mix public and private files in one bucket.
If you need both public and private storage, here are two options:
- Use separate buckets
- Store public files in one bucket and assign a custom domain to it using Cloudflare’s DNS and R2 settings.
- Store private files in another bucket and restrict direct access.
2. Use Cloudflare Workers for private access
- Instead of exposing the bucket directly, create a Cloudflare Worker that dynamically generates signed URLs for private files.
- This allows you to securely serve private assets while keeping public assets accessible.
This is one of the main differences between R2 and S3, so keep this in mind when designing your storage structure.
Conclusion
Cloudflare R2 is a solid choice for Laravel projects needing cost-effective storage, especially if you serve a lot of files. While Laravel doesn’t natively support R2, configuring it with the AWS S3 adapter works just fine. Just keep in mind R2’s bucket policy limitations when managing public and private files.
Now that your Laravel project is connected to Cloudflare R2, you can store, retrieve and serve files efficiently.