---
title: "Cloudflare R2 vs S3: What Egress Actually Costs You (and Where R2 Bills Instead)"
description: "Egress is the line item that decides whether you can leave a cloud. R2 charges nothing for it and S3 charges per gigabyte — but that is only half the story. A costed comparison of both bills, the fine print that moves the total on each side, and what the EU Data Act changed."
author: Aleksei Aleinikov
date: 2026-08-21
lang: en
tags: [cloudflare-r2, r2-pricing, s3-pricing, egress-fees, object-storage, cloud-costs]
canonical: https://www.alekseialeinikov.com/en/blog/topics/cloud/cloudflare-r2-vs-s3-egress-costs-2026
source: alekseialeinikov.com
---

# Cloudflare R2 vs S3: What Egress Actually Costs You (and Where R2 Bills Instead)

The bill for object storage is rarely about storage.

Look at a large S3 invoice and the gigabytes you keep are often the smaller half. The larger half is the gigabytes you *send* — and that line item does something no other line item does: it makes leaving expensive. A dataset you can't afford to move isn't just stored somewhere. It's held there.

Cloudflare R2 attacks that specific line by charging nothing for it. That's the headline, it's true, and it's not the whole story. Here is what each side actually bills, with the numbers from both vendors' own documentation, checked in August 2026.

## Why Egress Exists at All

Storing bytes is cheap and getting cheaper. Moving bytes across a provider's network edge is where the cost curve stops falling and where the pricing power sits.

The asymmetry is the tell. On S3, **data in from the internet is free**. Data out is not. So is data between buckets in the same region, data from S3 to any AWS service in the same region, and data out to CloudFront. Getting your data in costs nothing; getting it out to anywhere that isn't AWS costs per gigabyte.

That's not a conspiracy — bandwidth genuinely costs money — but the shape of it means the meter runs hardest precisely when you use your data somewhere else.

## The Headline Numbers

**S3** gives you the first **100 GB per month** of data transfer out to the internet free, aggregated across all AWS services and regions (excluding China and GovCloud). After that, you pay per gigabyte at a rate that varies by region and volume. AWS's own documentation, in a Multi-Region Access Point example, quotes **$0.09 per GB** for transfer out from Europe (Ireland) to the internet.

Cross-region transfer is cheaper but still metered: AWS quotes **$0.01/GB** from N. Virginia to Ohio and **$0.02/GB** from Oregon to N. Virginia.

**R2** charges **$0 for egress** on every storage class. Cloudflare's footnote defines the boundary precisely: egressing directly from R2 — through the Workers API, the S3-compatible API, or `r2.dev` domains — incurs no data transfer charge. If you attach other metered services to the bucket, those services may bill you.

At 5 TB of monthly egress from Ireland — comfortably inside the first pricing tier — that is roughly **$450 on one line**, before anyone has stored anything.

Resist the urge to scale that up by multiplication. AWS applies **volume tiers**, and states that rate tiers take into account your aggregate data transfer out across all AWS services. The per-gigabyte figure everyone quotes is the first tier; later gigabytes cost less. A flat multiplication across 50 TB overstates the bill — the direction is right, the total is not.

<figure>
  <img src="/blog/r2-vs-s3-bill-anatomy-2026.webp" alt="Side-by-side anatomy of an S3 bill and an R2 bill showing storage, operations and egress components, with egress metered on S3 and free on R2 while R2 shifts the weight onto Class A and Class B operations" width="1200" height="720" loading="lazy" decoding="async" />
  <figcaption>The same workload, two meters. R2 removes the egress line and puts more weight on operations.</figcaption>
</figure>

## What R2 Bills Instead

R2 is not free. It moves the meter from bytes-out to operations, and for some workloads that meter runs fast.

**Standard storage:**

| | Standard | Infrequent Access |
| --- | --- | --- |
| Storage | $0.015 / GB-month | $0.01 / GB-month |
| Class A operations | $4.50 / million | $9.00 / million |
| Class B operations | $0.36 / million | $0.90 / million |
| Data retrieval | None | $0.01 / GB |
| Egress | Free | Free |

The free tier covers 10 GB-month of storage, 1 million Class A and 10 million Class B operations per month — **Standard only**, not Infrequent Access.

The split matters more than the rates:

- **Class A** (expensive, mutating): `PutObject`, `CopyObject`, `ListObjects`, `ListBuckets`, `PutBucket`, the whole multipart family, lifecycle transitions, and `PutBucket*` configuration calls.
- **Class B** (cheap, reading): `GetObject`, `HeadObject`, `HeadBucket`, `UsageSummary`, `GetBucket*` calls.
- **Free**: `DeleteObject`, `DeleteBucket`, `AbortMultipartUpload`.

Note that **listing is Class A**. A job that lists a bucket on every iteration is paying the expensive rate for what feels like a read.

Cloudflare's own asset-hosting example makes the point better than any argument. Host 100,000 files averaging 100 KB, serve 10 million reads per day:

| | Usage | Billed |
| --- | --- | --- |
| Storage | 100,000 × 100 KB | **$0.00** (under free tier) |
| Class A | 100,000 writes | **$0.00** (under free tier) |
| Class B | 300 million reads | **$104.40** |
| **Total** | | **$104.40** |

Storage: nothing. Egress: nothing. The entire bill is read operations. If you migrate to R2 expecting a bill dominated by gigabytes, you will be surprised by a bill dominated by requests.

## The Fine Print That Moves the Total

Headline rates are the easy part. These are the clauses that change what you actually pay, and they cut both ways.

<figure>
  <img src="/blog/r2-vs-s3-fine-print-2026.webp" alt="Five billing details that inflate object storage costs: binary gigabytes on S3, LIST billed at PUT rate, charges for bytes sent after a dropped connection, R2 rounding usage up to the next billing unit, and minimum object size and duration rules" width="1200" height="740" loading="lazy" decoding="async" />
  <figcaption>None of these appear in a headline rate. All of them appear on the invoice.</figcaption>
</figure>

**S3 measures in binary gigabytes.** AWS states plainly that S3 storage usage is calculated in binary GB, where 1 GB is 2³⁰ bytes — a gibibyte. If you sized your budget with decimal gigabytes, you are off by about 7.4% before anything else happens.

**Egress is tiered, and the tiers are account-wide.** The headline per-gigabyte rate applies to the first tier only. AWS notes that rate tiers take into account your aggregate usage for data transfer out across all AWS services — so your S3 bill depends on what CloudFront, EC2 and everything else sent out that month. This cuts both ways: it makes single-rate estimates too high, and it makes per-service attribution harder than it looks.

**LIST is billed at the PUT rate on S3.** Not the GET rate. AWS says so directly: "LIST requests for any storage class are charged at the same rate as S3 Standard PUT, COPY, and POST requests." Paginating through a large bucket is not a cheap read operation.

**A dropped connection can cost more than you received.** This one is worth reading twice. AWS documents that if you request a 10 GB object and terminate after receiving 2 GB, S3 attempts to stop streaming but does not do so instantly — you may be billed for 3 GB. Clients that abort downloads aggressively pay for bytes nobody consumed.

**R2 rounds up to the next billing unit.** Cloudflare's examples: one million and one operations bills as **two million** operations; 1.1 GB-month bills as **2 GB-month**. On small accounts this rounding is proportionally brutal.

**Minimums bite on both sides.** S3 Standard-IA and One Zone-IA have a minimum billable object size of **128 KB** and a 30-day minimum duration; Glacier tiers run 90 or 180 days and add 40 KB of metadata per archived object. R2's Infrequent Access carries a **30-day minimum duration**.

**Unauthorized requests are free on R2.** Cloudflare states you are not charged for operations where the caller lacks permission (HTTP 401). Worth knowing if your bucket is publicly addressable and attracting noise.

## When Infrequent Access Costs More Than Standard

The clearest trap in R2's pricing is its own cheaper tier. Cloudflare publishes both examples side by side.

Write 1,000 objects of 1 GB, read each 1,000 times over the month:

- **Standard**: $14.85 total (990 billable GB-months after free tier; operations covered by free tier)
- **Infrequent Access**: **$29.90** total

IA is twice the price. The mechanics: IA has no free tier, its Class A operations cost double, its Class B cost 2.5×, retrieval adds $0.01/GB, and the 30-day minimum means storing objects for five days still bills 1,000 GB-months rather than 167.

The rule that falls out: **Infrequent Access is a bet that you will not read the data.** Break that bet and you pay more than you would have on Standard.

## What the EU Data Act Changed

Regulation (EU) 2023/2854 — the Data Act — entered into force on 11 January 2024 and **has applied since 12 September 2025**. Among its measures are rules letting customers switch effectively between providers of data-processing services, aimed explicitly at unlocking the EU cloud market.

The interesting part is how that shows up in a price list. AWS's S3 pricing page now carries this line:

> EU customers may request reduced data transfer rates for eligible use cases under the European Data Act. Please contact AWS Customer Support for more information.

Read it carefully. The relief exists, and it is **a request you have to make**, for **eligible** use cases, through **support**. It is not a rate change applied to your account because you are in the EU.

That is the practical shape of the regulation today: the lock-in mechanism is now negotiable rather than absent. If you are planning a migration out of AWS from the EU, opening that support case is a concrete, dated step in your plan — not a footnote.

## So Which One

The decision is not "which is cheaper." It is **which meter matches your traffic**.

**R2 wins decisively when bytes leave the cloud.** Public asset delivery, media, downloads, backups pulled by systems outside AWS, anything served to the open internet at volume. This is the case R2 was built for and the numbers aren't close.

**S3 stays sensible when bytes never leave.** If your consumers are EC2, Lambda, Athena or Glue in the same region, you are already paying zero egress. Migrating that workload to R2 to save on a line you aren't being charged for is motion without progress.

**Count operations before you commit.** A workload with many small objects and high request rates can cost more on R2 than the egress it saves. Cloudflare's own $104.40 example is entirely operations.

**Check what you lose.** R2 speaks the S3 API, but "S3-compatible" is not "S3-identical." Before migrating, verify the specific features your stack depends on rather than assuming parity.

**Migration tooling is free, mostly.** Cloudflare's Super Slurper (bulk copy) and Sippy (incremental, migrate-on-read) cost nothing to use — you pay only the Class A operations they generate against your R2 bucket. Your *source* bucket may still bill you for the reads and the egress on the way out, which is the last irony of the whole arrangement: leaving costs egress.

## A Checklist Before You Move

1. Pull last month's actual egress in GB from Cost Explorer — not an estimate.
2. Count your requests, split into writes/lists (Class A) and reads (Class B). Multiply by R2's rates.
3. Check how much of your traffic is same-region AWS-to-AWS. That part is already free and gains nothing.
4. Re-measure storage in binary vs decimal gigabytes so you compare like with like.
5. If any bucket uses IA or Glacier, check minimum-duration and minimum-object-size penalties before transitioning anything.
6. If you are in the EU, open the AWS support case about Data Act transfer rates — get the number in writing before you model the migration.
7. Budget the one-time cost of the migration itself: reads plus egress on the source side.

## The Real Point

Egress pricing is not really a bandwidth charge. It is the mechanism that converts stored data into retained customers, which is why a competitor attacking exactly that line was always going to get attention — and why a regulator eventually did too.

But "free egress" is a claim about one line on an invoice, not about the invoice. Cloudflare bills you for operations, rounds usage up, and penalises the wrong storage class just as firmly as AWS does. The provider changed; the need to read the fine print did not.

Price your own workload against both meters. The answer is different for a media CDN and for an analytics pipeline that never leaves us-east-1, and anyone who tells you otherwise is quoting a headline rate.

---

**Related reading:** on keeping data movable in the first place, see [backups are not disaster recovery](https://www.alekseialeinikov.com/en/blog/topics/cloud/backups-are-not-disaster-recovery-2026) and [sovereign by design](https://www.alekseialeinikov.com/en/blog/topics/architecture/sovereign-by-design-building-a-sovereign-platform-on-kubernetes-2026). For the analytics side of storage economics, see [BigQuery vs Snowflake](https://www.alekseialeinikov.com/en/blog/topics/data/bigquery-vs-snowflake-2026-honest-comparison).

**Sources:** all figures come from vendor documentation checked in August 2026 — [Cloudflare R2 pricing](https://developers.cloudflare.com/r2/pricing/) (updated 7 August 2026), [Amazon S3 pricing](https://aws.amazon.com/s3/pricing/), and the European Commission's [Data Act](https://digital-strategy.ec.europa.eu/en/policies/data-act) page. Rates change; verify against the vendor calculators before committing a budget.
