Cloudflare R2 vs S3 + CloudFront: Honest Cost Comparison
Cloudflare R2 has no egress fees. Workers charge per request. Here's an honest cost comparison against the AWS edge stack — and when Cloudflare actually wins.
By Andrii Votiakov on
Cloudflare's pitch is simple: no egress fees on R2, Workers at the edge instead of Lambda@Edge or CloudFront Functions, and a flat pricing model that doesn't penalise you for moving data around. It sounds compelling. But "no egress" is only part of the story, and I've seen teams switch to Cloudflare expecting big savings and find the bill was roughly the same — or higher — because they modelled it wrong.
Here's the actual comparison, with real numbers.
Quick answer
Cloudflare R2 + Workers beats S3 + CloudFront + Lambda for workloads with high egress volume (>5 TB/month), frequent small object reads, or global edge compute with low cold-start requirements. S3 + CloudFront wins on deep AWS integration, complex IAM / access control, large sequential reads, and workloads under ~1 TB/month egress where CloudFront's free tier absorbs most costs. The crossover point is usually around $400-600/month in AWS egress costs.
How the pricing models differ
S3 + CloudFront + Lambda@Edge (AWS)
| Component | Pricing |
|---|---|
| S3 storage | $0.023/GB/month (Standard) |
| S3 GET requests | $0.0004 per 1,000 |
| CloudFront egress | $0.0085-0.085/GB (tiered, region-dependent) |
| CloudFront HTTPS requests | $0.0100 per 10,000 |
| Lambda@Edge | $0.60/million requests + $0.00005001/GB-second |
| Data transfer S3 → CloudFront | Free (same-region) |
CloudFront gives you 1 TB free egress per month on the free tier. That absorbs a lot of small workloads. Above 1 TB, you start paying $0.085/GB in the US, dropping to $0.0085/GB at >5 PB/month.
Cloudflare R2 + Workers
| Component | Pricing |
|---|---|
| R2 storage | $0.015/GB/month |
| R2 Class A operations (writes, lists) | $4.50 per million |
| R2 Class B operations (reads) | $0.36 per million |
| R2 egress to internet | $0 |
| Workers free tier | 100,000 requests/day |
| Workers Paid | $5/month + $0.50 per million requests over 10M |
| Workers CPU | $0.02 per million CPU-milliseconds over 30M |
The absence of egress fees on R2 is real. Data leaving R2 to the internet costs nothing — not the reduced rate AWS charges at scale, genuinely zero.
Scenario 1: Image CDN, 5 TB/month served
A typical setup: user-uploaded images stored in S3/R2, served globally via CDN/edge, with light image transformation at the edge.
AWS (S3 + CloudFront):
- Storage: 500 GB × $0.023 = $11.50
- CloudFront egress: 5 TB × $0.085/GB (US rate) = $425
- CloudFront requests: 50M × $0.010/10k = $50
- Lambda@Edge (resize): 50M × $0.60/M + compute = $30 + $25 = $55
Total: ~$541/month
Cloudflare (R2 + Workers):
- Storage: 500 GB × $0.015 = $7.50
- R2 reads: 50M × $0.36/M = $18
- Workers (transforms): $5/month base + 50M at $0.50/M over 10M = $5 + $20 = $25
- Egress: $0
Total: ~$50.50/month
At this scale, Cloudflare is roughly 10x cheaper. The difference is almost entirely egress.
Scenario 2: Small workload, 200 GB/month served
AWS (S3 + CloudFront):
- Storage: 50 GB × $0.023 = $1.15
- CloudFront egress: 200 GB — within free tier (1 TB/month) = $0
- Requests: 2M × $0.010/10k = $2
Total: ~$3.15/month (CloudFront free tier covers the egress entirely)
Cloudflare (R2 + Workers):
- Storage: 50 GB × $0.015 = $0.75
- R2 reads: 2M × $0.36/M = $0.72
- Workers: within free tier = $0
Total: ~$1.47/month
Cloudflare is still cheaper, but the difference is cents. At this scale, the deciding factor is where the rest of your infrastructure lives — staying on AWS is almost certainly the right call.
Scenario 3: Global API at the edge, 10M requests/month
This is a common Workers use case: a lightweight API (auth checks, A/B routing, personalisation) running at the edge rather than in a central Lambda or EC2 fleet.
AWS (CloudFront Functions + Lambda@Edge):
- CloudFront Functions: 10M × $0.10/M = $1
- Lambda@Edge (if needed for heavier logic): 10M × $0.60/M + compute ~$6-15
Total: $7-16/month
Cloudflare Workers:
- 10M requests, Workers Paid: $5/month flat + 0 overage (within 10M included)
Total: $5/month
Workers wins on price here, and also on DX (developer experience). Workers cold starts are sub-millisecond; Lambda@Edge cold starts are 50-200ms. For latency-sensitive edge compute, that difference matters more than the price.
When Cloudflare wins
- High egress volume (>5 TB/month): egress fees on AWS dominate; R2's zero-egress model saves real money
- Global media delivery: images, video previews, documents served to a global audience
- Edge compute at high request rates: Workers is genuinely cheaper than Lambda@Edge at >10M requests/month
- Static site hosting at scale: Pages + R2 is a simple, cheap combination
- Multi-cloud or AWS-exit scenarios: R2's S3-compatible API makes migration manageable
When AWS wins
- Deep AWS service integration: If your R2 data needs to talk to RDS, SQS, SNS, or EventBridge, staying in S3 avoids a cross-cloud hop
- Complex IAM and access patterns: S3 bucket policies, presigned URLs, and IAM are more mature than R2's equivalent
- Large sequential reads (analytics, ML training): S3's Select and the overall S3-to-analytics pipeline (Athena, Glue, SageMaker) has no Cloudflare equivalent
- Compliance and certifications: AWS has more certifications relevant to regulated industries
- Under 1 TB/month egress: CloudFront's free tier makes the cost difference negligible
The migration path
If you're moving from S3 + CloudFront to R2, the migration is less painful than it sounds. R2 exposes an S3-compatible API — most SDKs work by changing the endpoint and credentials. The main work is:
- Copy objects to R2 using
rcloneor the R2 migration tool - Update your application endpoint configuration
- Set up a Workers route or a custom domain pointing to R2
- Validate behaviour (redirect handling, CORS, presigned URL equivalents)
Presigned URLs work differently in R2 — they're supported but have a 7-day maximum expiry vs S3's 7 days as well (same limit, different implementation details to test).
Realistic numbers
A client running a media-heavy SaaS (~8 TB/month served, 60M requests/month):
| Component | AWS monthly cost | Cloudflare monthly cost |
|---|---|---|
| Storage (1.2 TB) | $27.60 | $18 |
| Egress/CDN | $680 | $0 |
| Requests + compute | $90 | $35 |
| Total | $797/month | $53/month |
Migration took 4 days. Ongoing saving: ~$744/month, ~$8,900/year.
The workload was a good fit: high egress, no AWS service dependencies, no complex IAM requirements. Not all migrations look this clean.
If you're trying to work out whether your current CDN and edge stack is overkill or under-optimised, I'm happy to run the numbers with you. Book a call.