From ae542cff84ddcdcd5e417d0c459556f18b5988e1 Mon Sep 17 00:00:00 2001 From: krys-cf Date: Tue, 18 Aug 2026 11:52:03 -0500 Subject: [PATCH 1/3] docs(r2): clarify billing for failed 4xx requests in pricing FAQ Expand the R2 pricing FAQ to explain which failed requests are billed as Class A operations. The previous FAQ only mentioned 401 Unauthorized as exempt, leaving customers uncertain about other 4xx status codes. Empirically tested against R2 analytics API: - Auth-layer failures (401, 403 SignatureDoesNotMatch/ExpiredRequest/ RequestTimeTooSkewed) are NOT billed - Post-auth failures (412 PreconditionFailed, 400 BadDigest) ARE billed DEE-3763 --- src/content/docs/r2/pricing.mdx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/content/docs/r2/pricing.mdx b/src/content/docs/r2/pricing.mdx index 2f2fd705198..0d1da2f06e4 100644 --- a/src/content/docs/r2/pricing.mdx +++ b/src/content/docs/r2/pricing.mdx @@ -164,8 +164,16 @@ To learn more about how usage is billed, refer to [Cloudflare Billing Policy](/b ## Frequently asked questions -### Will I be charged for unauthorized requests to my R2 bucket? +### Will I be charged for failed requests to my R2 bucket? -No. You are not charged for operations when the caller does not have permission to make the request (HTTP 401 `Unauthorized` response status code). +Requests that fail at the authentication layer are not charged. This includes: + +- HTTP 401 (`Unauthorized`) — missing or invalid credentials. +- HTTP 403 when caused by an authentication failure such as `SignatureDoesNotMatch`, `ExpiredRequest`, or `RequestTimeTooSkewed`. + +Requests that pass authentication but fail for other reasons are counted and billed according to the operation's class. For example: + +- A `PutObject` returning HTTP 412 (`PreconditionFailed`) due to a conditional header like `If-None-Match` counts as a Class A operation. +- A `PutObject` returning HTTP 400 (`BadDigest`) due to a mismatched `Content-MD5` counts as a Class A operation. [^1]: Egressing directly from R2, including via the [Workers API](/r2/api/workers/), [S3 API](/r2/api/s3/), and [`r2.dev` domains](/r2/buckets/public-buckets/#enable-managed-public-access) does not incur data transfer (egress) charges and is free. If you connect other metered services to an R2 bucket, you may be charged by those services. From e6fa5c282b5c04e48b17c83970d3b318edaec289 Mon Sep 17 00:00:00 2001 From: krys-cf Date: Tue, 8 Sep 2026 10:52:13 -0500 Subject: [PATCH 2/3] docs(r2): clarify unbilled authorization failures --- src/content/docs/r2/pricing.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/content/docs/r2/pricing.mdx b/src/content/docs/r2/pricing.mdx index 0d1da2f06e4..7a0450a2d2c 100644 --- a/src/content/docs/r2/pricing.mdx +++ b/src/content/docs/r2/pricing.mdx @@ -166,12 +166,13 @@ To learn more about how usage is billed, refer to [Cloudflare Billing Policy](/b ### Will I be charged for failed requests to my R2 bucket? -Requests that fail at the authentication layer are not charged. This includes: +Requests that fail during authentication or authorization are not charged. This includes: - HTTP 401 (`Unauthorized`) — missing or invalid credentials. - HTTP 403 when caused by an authentication failure such as `SignatureDoesNotMatch`, `ExpiredRequest`, or `RequestTimeTooSkewed`. +- HTTP 403 (`AccessDenied`) when valid credentials do not have permission to perform the operation. -Requests that pass authentication but fail for other reasons are counted and billed according to the operation's class. For example: +Requests that pass authentication and authorization but fail for other reasons are counted and billed according to the operation's class. For example: - A `PutObject` returning HTTP 412 (`PreconditionFailed`) due to a conditional header like `If-None-Match` counts as a Class A operation. - A `PutObject` returning HTTP 400 (`BadDigest`) due to a mismatched `Content-MD5` counts as a Class A operation. From 07ec87d4f0b2bb3dd731f801af428f16d6c1b71d Mon Sep 17 00:00:00 2001 From: krys-cf Date: Thu, 17 Sep 2026 10:37:11 -0500 Subject: [PATCH 3/3] Update src/content/docs/r2/pricing.mdx Co-authored-by: Riccardo Di Maio <35903974+rdimaio@users.noreply.github.com> --- src/content/docs/r2/pricing.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/r2/pricing.mdx b/src/content/docs/r2/pricing.mdx index 7a0450a2d2c..8c0ec916770 100644 --- a/src/content/docs/r2/pricing.mdx +++ b/src/content/docs/r2/pricing.mdx @@ -172,7 +172,7 @@ Requests that fail during authentication or authorization are not charged. This - HTTP 403 when caused by an authentication failure such as `SignatureDoesNotMatch`, `ExpiredRequest`, or `RequestTimeTooSkewed`. - HTTP 403 (`AccessDenied`) when valid credentials do not have permission to perform the operation. -Requests that pass authentication and authorization but fail for other reasons are counted and billed according to the operation's class. For example: +Some requests that pass authentication and authorization but fail for other reasons are counted and billed according to the operation's class. For example: - A `PutObject` returning HTTP 412 (`PreconditionFailed`) due to a conditional header like `If-None-Match` counts as a Class A operation. - A `PutObject` returning HTTP 400 (`BadDigest`) due to a mismatched `Content-MD5` counts as a Class A operation.