Does Laravel Work With Cloudflare R2?
Laravel works seamlessly with Cloudflare R2 through S3-compatible APIs, making it a natural fit for file storage without vendor lock-in.
Quick Facts
How Laravel Works With Cloudflare R2
Laravel's Storage facade abstracts cloud storage through flysystem drivers, and R2's S3-compatible API means you configure it exactly like AWS S3 with different credentials and endpoint. No special Laravel package required—just update your `.env` and config/filesystems.php`. The integration is transparent: all your existing `Storage::put()`, `Storage::get()`, and `Storage::delete()` calls work identically.
Developers appreciate R2 for Laravel projects because egress fees disappear entirely, making media-heavy applications dramatically cheaper. You're trading nothing in developer experience—authentication, multipart uploads, signed URLs, and streaming downloads all work as expected. The S3-compatible nature means you can even swap between AWS S3 and R2 by changing environment variables, useful for hybrid cloud strategies or cost optimization.
One architectural consideration: R2's eventual consistency model is slightly looser than S3, so if you're deleting and immediately re-creating objects with identical keys in rapid succession, add minimal delays. For typical web applications (user avatars, document uploads, media libraries), this is never a practical issue.
Best Use Cases
Quick Setup
composer require aws/aws-sdk-php (usually already included via league/flysystem-aws-s3-v3)# .env
FILESYSTEM_DISK=r2
AWS_ACCESS_KEY_ID=your_r2_access_key
AWS_SECRET_ACCESS_KEY=your_r2_secret_key
AWS_DEFAULT_REGION=auto
AWS_BUCKET=your-bucket-name
AWS_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
AWS_USE_PATH_STYLE_ENDPOINT=true
# config/filesystems.php
'disks' => [
'r2' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT'),
],
]
# In your controller/model
Storage::disk('r2')->put('avatars/user123.jpg', $imageContent);
$url = Storage::disk('r2')->url('avatars/user123.jpg');Known Issues & Gotchas
R2 doesn't support some advanced S3 features like object lock, legal hold, or bucket policies with the same syntax
Fix: Check Cloudflare's R2 limitations documentation before using advanced S3 SDK features; stick to core operations (put/get/delete/list)
Signed URL expiration times may behave differently; R2 has different max expiration limits than S3
Fix: Test your signed URL TTL values in R2 and adjust if needed; keep expiration times under 7 days for maximum compatibility
Missing `AWS_BUCKET` region in `.env` can cause authentication failures with R2 endpoints
Fix: Always explicitly set AWS_REGION even though R2 is regionless; use 'auto' or any valid AWS region string
Alternatives
- •AWS S3 + Laravel Storage (industry standard, more features, higher egress costs)
- •DigitalOcean Spaces + Laravel Storage (similar S3 compatibility, regional pricing model)
- •MinIO self-hosted + Laravel Storage (full control, on-premises, higher operational overhead)
Resources
Related Compatibility Guides
Explore more compatibility guides