Does FastAPI Work With Cloudflare R2?
FastAPI and Cloudflare R2 integrate seamlessly—use the boto3 library to treat R2 as S3-compatible storage within your FastAPI application.
Quick Facts
How FastAPI Works With Cloudflare R2
FastAPI has no built-in R2 support, but R2's S3 compatibility means you leverage boto3 (the standard AWS SDK for Python) with minimal configuration changes. You point boto3 at R2's endpoint instead of AWS S3, authenticate with your R2 API tokens, and everything works identically. This is a common pattern—many developers use R2 as a drop-in S3 replacement specifically because of this compatibility.
The developer experience is straightforward: install boto3, configure credentials as environment variables, and use it in FastAPI route handlers or background tasks. R2 shines here because you avoid egress fees, making it ideal for APIs that serve files frequently. You can upload files from requests, generate signed URLs for direct downloads, or stream large objects without loading everything into memory. FastAPI's async support pairs well with boto3's threading, though you'll want to run boto3 calls in a thread pool to avoid blocking the event loop.
Best Use Cases
FastAPI + R2 File Upload
pip install fastapi uvicorn boto3 python-multipartimport boto3
from fastapi import FastAPI, UploadFile, File
from concurrent.futures import ThreadPoolExecutor
import os
app = FastAPI()
executor = ThreadPoolExecutor(max_workers=4)
s3_client = boto3.client(
's3',
endpoint_url=os.getenv('R2_ENDPOINT'),
aws_access_key_id=os.getenv('R2_ACCESS_KEY'),
aws_secret_access_key=os.getenv('R2_SECRET_KEY'),
region_name='auto'
)
@app.post('/upload')
async def upload_file(file: UploadFile = File(...)):
loop = app.router.routes
def upload_to_r2():
s3_client.upload_fileobj(
file.file,
os.getenv('R2_BUCKET'),
file.filename,
ExtraArgs={'ContentType': file.content_type}
)
return s3_client.generate_presigned_url(
'get_object',
Params={'Bucket': os.getenv('R2_BUCKET'), 'Key': file.filename},
ExpiresIn=3600
)
import asyncio
url = await asyncio.get_event_loop().run_in_executor(executor, upload_to_r2)
return {'url': url, 'filename': file.filename}Known Issues & Gotchas
boto3 blocks the event loop on network I/O, causing performance issues under high concurrency
Fix: Wrap boto3 calls with `loop.run_in_executor()` or use libraries like `aioboto3` for true async support
R2 endpoint URL format differs by region; using the wrong endpoint causes authentication failures
Fix: Use the account-specific endpoint: `https://<account-id>.r2.cloudflarestorage.com` and verify in Cloudflare dashboard
Signed URLs expire after the specified duration; requests after expiry fail silently
Fix: Set appropriate TTL values (default 1 hour) and implement refresh logic or generate URLs on-demand
CORS headers must be configured in R2 bucket settings if accessing files directly from browser
Fix: Configure CORS policy in Cloudflare R2 console or use presigned POST requests for uploads
Alternatives
- •AWS S3 + FastAPI: Native AWS integration with more features but egress costs; use if you need advanced AWS services
- •MinIO + FastAPI: Self-hosted S3-compatible storage; ideal for on-premise deployments with full control
- •Google Cloud Storage + FastAPI: Alternative cloud storage with strong Python library support and different pricing model
Resources
Related Compatibility Guides
Explore more compatibility guides