Skip to content

[Cache] Document CDN-Cache-Control as a BYPASS cause - #32569

Merged
zaidoon1 merged 6 commits into
productionfrom
ngayerie/cache-cdn-cache-control-bypass
Aug 21, 2026
Merged

[Cache] Document CDN-Cache-Control as a BYPASS cause#32569
zaidoon1 merged 6 commits into
productionfrom
ngayerie/cache-cdn-cache-control-bypass

Conversation

@ngayerie

@ngayerie ngayerie commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Two small follow-ups to #32423.

What

  • Add a bullet to the BYPASS lists in cache-responses.mdx and investigating-uncached-responses.mdx documenting Cloudflare-CDN-Cache-Control and CDN-Cache-Control, with the precedence chain Cloudflare-CDN-Cache-Control > CDN-Cache-Control > Cache-Control. Uses a concrete example so the reader sees that a cacheable Cache-Control alongside a non-cacheable CDN-Cache-Control still produces BYPASS.
  • Rewrite the Age callout in cache-responses.mdx and the corresponding HIT-verification bullet in the troubleshooting page. Both currently mention CacheTieredFill=true, which is an http_requests Logpush field, not an HTTP header — readers cannot observe it on a response.

Why

Neither BYPASS list mentioned Cloudflare-CDN-Cache-Control or CDN-Cache-Control. When both Cache-Control and CDN-Cache-Control are set, CDN-Cache-Control wins per CDN-Cache-Control, so a reader debugging a BYPASS while only looking at Cache-Control has no signal from these pages that another header is in play.

@zaidoon1 for review.

@ngayerie
ngayerie requested review from a team, ack-cf and zaidoon1 as code owners August 6, 2026 09:29
@cloudflare-docs-bot

cloudflare-docs-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review

⚠️ 1 warning found in commit 7642ef2.

👉 Fix in your agent 👈
Fix the following review findings in PR #32569 (https://github.com/cloudflare/cloudflare-docs/pull/32569).

Before making changes, review each finding and present a brief summary table:
- For each finding, state whether you agree, disagree, or need clarification
- If you disagree (e.g. the fix requires disproportionate effort for minimal benefit,
  or the finding is factually incorrect), explain why
- If you need clarification before deciding, ask those questions
- Then share your plan for which issues to tackle and in what order

After triaging, follow this order:
1. Post a comment on this PR for any findings you are skipping, with the finding ID and your reasoning.
2. Then commit the fixes for the legitimate findings.

The comment must come before the commit — the bot reads PR comments when a new
push triggers a review, so skip comments posted after the push will be missed.

---

## Code Review

### Warnings (1)

#### CR-d79577e2bb37 · Incomplete Authorization caveat for Cloudflare-CDN-Cache-Control
- **File:** `src/content/docs/cache/troubleshooting/investigating-uncached-responses.mdx` line 44
- **Issue:** The new bullet says `no-cache`, `max-age=0`, or `s-maxage=0` in `Cloudflare-CDN-Cache-Control` or `CDN-Cache-Control` ‘do not produce `BYPASS`’. However, the existing Origin Cache Control docs state that when `Cloudflare-Cdn-Cache-Control` is set, OCC is forced on and the Authorization logic allows caching only if `s-maxage`, `must-revalidate`, or `public` is also present. So a request that includes an `Authorization` header and `Cloudflare-CDN-Cache-Control: no-cache` or `max-age=0` would still return `BYPASS`, contradicting the unqualified claim.
- **Fix:** Qualify the statement so it does not contradict the Authorization interaction — for example, add ‘unless the request includes an `Authorization` header’ or cross-reference the existing Authorization bullet.

Code Review

This code review is in beta and may not always be helpful — use your judgment.

Warnings (1)
File Issue
cache/troubleshooting/investigating-uncached-responses.mdx line 44 Incomplete Authorization caveat for Cloudflare-CDN-Cache-Control — The new bullet says no-cache, max-age=0, or s-maxage=0 in Cloudflare-CDN-Cache-Control or CDN-Cache-Control ‘do not produce BYPASS’. However, the existing Origin Cache Control docs state that when Cloudflare-Cdn-Cache-Control is set, OCC is forced on and the Authorization logic allows caching only if s-maxage, must-revalidate, or public is also present. So a request that includes an Authorization header and Cloudflare-CDN-Cache-Control: no-cache or max-age=0 would still return BYPASS, contradicting the unqualified claim. Fix: Qualify the statement so it does not contradict the Authorization interaction — for example, add ‘unless the request includes an Authorization header’ or cross-reference the existing Authorization bullet.

Conventions

No convention issues found.

Style Guide Review

No style-guide issues found.

Commands

Only codeowners can run commands. Post a comment with the command to trigger it.

Command Description
/review Runs a review now. Incremental if a prior review exists, full if not.
/full-review Re-reviews the entire PR diff from scratch, ignoring incremental history. Useful after a rebase, when you want a fresh review, or if the bot gets out of sync and reports issues that no longer exist.
/ignore-review-limit Permanently lifts the 2-review automatic limit for this PR. Future pushes will trigger reviews as normal.
/disable-auto-review Stops automatic reviews from triggering on future pushes to this PR. Codeowners can still run /review or /full-review manually.
/rebase Rebases the PR branch against production. On conflict, attempts to resolve automatically using AI. Stops with an explanation if confidence is not high enough.

@github-actions github-actions Bot added product:cache Issues or PRs related to Cache size/xs labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

This pull request requires reviews from CODEOWNERS as it changes files that match the following patterns:

Pattern Owners
/src/content/docs/cache/ @cloudflare/product-owners, @ack-cf, @zaidoon1, @mbullock1986

Comment thread src/content/docs/cache/concepts/cache-responses.mdx Outdated
Comment thread src/content/docs/cache/concepts/cache-responses.mdx Outdated
Common reasons the origin response is treated as not cacheable include:

* The response exceeds the [maximum cacheable file size](/cache/concepts/default-cache-behavior/#cacheable-size-limits) for your plan.
* The origin returned a `Cloudflare-CDN-Cache-Control` or `CDN-Cache-Control` header with a non-cacheable directive. Cloudflare evaluates these headers ahead of `Cache-Control`, in the precedence `Cloudflare-CDN-Cache-Control` > `CDN-Cache-Control` > `Cache-Control`. For example, an origin returning both `Cache-Control: public, max-age=3600` and `CDN-Cache-Control: no-store` produces `BYPASS`. Inspect every response header, not just `Cache-Control`. Refer to [CDN-Cache-Control](/cache/concepts/cdn-cache-control/) for the precedence rules.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call, maybe we want to mention Surrogate-Control too.

Cloudflare-CDN-CC > CDN-CC > Surrogate-Control > Cache-Control

cc @zaidoon1

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mbullock1986 , @zaidoon1
What do you think? Should we mention Surrogate-Control in this page?

@foreseaz

foreseaz commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Both changes LGTM. Left some nits to make the existing change more clear, thanks for the effort @ngayerie

@ngayerie
ngayerie force-pushed the ngayerie/cache-cdn-cache-control-bypass branch from 7ebf475 to 2f583e2 Compare August 17, 2026 15:34
@ngayerie
ngayerie requested a review from mbullock1986 as a code owner August 17, 2026 15:34
@ngayerie

Copy link
Copy Markdown
Collaborator Author

Hi @mbullock1986 @zaidoon1
Would it be possible for you to review/approve this PR?
Thanks!

@ngayerie
ngayerie force-pushed the ngayerie/cache-cdn-cache-control-bypass branch from 2f583e2 to ee9f456 Compare August 17, 2026 19:12

@zaidoon1 zaidoon1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the CDN-Cache-Control and Age claims against actual behavior. The BYPASS bullet's core point is right, but a few details need tightening. Comments inline.

Comment thread src/content/docs/cache/concepts/cache-responses.mdx Outdated
Comment thread src/content/docs/cache/concepts/cache-responses.mdx Outdated
Comment thread src/content/docs/cache/concepts/cache-responses.mdx Outdated
Comment thread src/content/docs/cache/concepts/cache-responses.mdx Outdated
Comment thread src/content/docs/cache/troubleshooting/investigating-uncached-responses.mdx Outdated

Copy link
Copy Markdown
Collaborator Author

[AI] Thanks @zaidoon1 — all 5 comments applied in 26b189cb.

cache-responses.mdx

  1. Age callout scope — dropped at that cache, kept it network-wide, and added the Tiered Cache clarification (HIT served locally can carry an Age inherited from an upper tier, so the value can be older than the last local fill).
  2. Age status enumeration — rewritten by cf-cache-status: Age is set on HIT, STALE, UPDATING. It is absent on MISS, on EXPIRED and REVALIDATED when the request revalidated against the origin, and on DYNAMIC, BYPASS, NONE/UNKNOWN. Origin-set Age on an uncacheable response is proxied to the client unchanged.
  3. CDN-Cache-Control BYPASS bullet — named the directives (only no-store and bare private produce BYPASS; no-cache/max-age=0/s-maxage=0 produce MISS then REVALIDATED/EXPIRED), dropped the Origin Cache Control condition (these headers follow OCC-style semantics regardless of the zone setting).
  4. CDN-Cache-Control BYPASS bullet — added the Edge Cache TTL override, same as Cache-Control: no-store.

investigating-uncached-responses.mdx

  1. CDN-Cache-Control BYPASS bullet — mirrored the same corrections, and reframed the troubleshooting angle: noted that Cloudflare does not forward Cloudflare-CDN-Cache-Control to the client, so if a reader sees BYPASS without a visible CDN-Cache-Control header the next step is checking what the origin actually sent (or dropping the header at origin and retesting), not "inspect every response header".

Also reconciled the HIT verification bullet on the same page with your Age scope correction — a fresh local fill from an upper tier can already report a large Age, so "may be absent" was wrong.

@zaidoon1 zaidoon1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latest addresses everything I raised. One clause got lost in the rewrite that I'd put back, plus a couple of small things.

Comment thread src/content/docs/cache/troubleshooting/investigating-uncached-responses.mdx Outdated
Comment thread src/content/docs/cache/concepts/cache-responses.mdx Outdated
Comment thread src/content/docs/cache/concepts/cache-responses.mdx Outdated
Comment thread src/content/docs/cache/troubleshooting/investigating-uncached-responses.mdx Outdated

Copy link
Copy Markdown
Collaborator Author

[AI] Thanks @zaidoon1 — all 4 comments applied in f1675c05.

  1. Age reset rule — qualified as revalidation against the origin, matching the phrasing on the paragraph below. A lower tier revalidating against an upper tier that still has the object picks up the upper tier's age, so the unqualified revalidation was wrong for Tiered Cache.
  2. Semicolon on cache-responses.mdx line 58 — split into two sentences.
  3. Six-sentence bullet on the troubleshooting page — pulled the no-cache/max-age=0/s-maxage=0 clarification into its own bullet. It's the clause that tells the reader to look under Repeated MISS instead, so it earns the separate bullet. That split also removed the matching semicolon on the troubleshooting page.
  4. HIT verify Age bullet — restored the absent-on-fill case: on the request that fills a lower-tier data center from an upper tier, the client gets HIT with no Age at all, because the local data center has not served it from cache yet. Subsequent requests include Age, and with Tiered Cache the value can already be large — kept the "increases on subsequent requests" signal you called out.

@zaidoon1 zaidoon1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two small ones on the new bullet, neither blocking.

Comment thread src/content/docs/cache/troubleshooting/investigating-uncached-responses.mdx Outdated
Comment thread src/content/docs/cache/troubleshooting/investigating-uncached-responses.mdx Outdated

Copy link
Copy Markdown
Collaborator Author

[AI] Thanks @zaidoon1 — both applied in 6afd6b1b.

  • "these headers" reference — replaced with the explicit header names at the start of the bullet, so it stands alone for a scanner reading only the bolded openings.
  • Pointer to Repeated MISS — dropped. That section covers cache key variance and eviction, neither of which matches the MISSREVALIDATED/EXPIRED pattern this bullet describes, so it was sending readers the wrong way. Kept the sequence description — Confirm the response reaches cache already covers verification with those same two statuses.

@ngayerie
ngayerie force-pushed the ngayerie/cache-cdn-cache-control-bypass branch from 6afd6b1 to f2daf62 Compare August 19, 2026 09:34

@mbullock1986 mbullock1986 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on Zaidoon's comments being fixed

@ngayerie

Copy link
Copy Markdown
Collaborator Author

Hi @zaidoon1
Happy for me to merge?

- Add a BYPASS bullet documenting Cloudflare-CDN-Cache-Control and
  CDN-Cache-Control, and their precedence over Cache-Control. An
  origin returning cacheable Cache-Control alongside a non-cacheable
  CDN-Cache-Control directive produces BYPASS, and readers otherwise
  have no signal that these two headers even exist as BYPASS causes.
- Rewrite the Age callout and HIT confirmation to describe the
  Tiered Cache lower-tier fill in plain language, without referring
  to the internal CacheTieredFill=true field. That field is only
  visible in the http_requests Logpush dataset, not in HTTP
  responses, so it is not something readers can inspect.
Address SG-619a15c3d17a from the docs bot review.
Clarify that Age is per-data-center scope: the seconds since the
serving data center's local cache admitted or last revalidated the
object, rather than the vaguer 'time in Cloudflare's cache'.

Applies @foreseaz's suggestion.
cache-responses.mdx:
- Age callout scope: drop 'at that cache' — Age tracks the object's age
  in Cloudflare's network-wide cache, not a single data center. A HIT
  served locally can carry an Age inherited from an upper tier under
  Tiered Cache.
- Age status enumeration: rewrite by cf-cache-status. Age is set on
  HIT, STALE, UPDATING. It is absent on MISS, on EXPIRED and REVALIDATED
  when the request revalidated against the origin, and on DYNAMIC,
  BYPASS, NONE/UNKNOWN. Origin-set Age on uncacheable responses is
  proxied to the client — a DYNAMIC or BYPASS response can carry an
  Age value that came from the origin, not from Cloudflare.
- CDN-Cache-Control BYPASS bullet:
    - Name the directives: only no-store and bare private produce
      BYPASS. no-cache, max-age=0, s-maxage=0 do not — they produce
      MISS then REVALIDATED or EXPIRED.
    - Add the Edge Cache TTL override, same as for Cache-Control:
      no-store.
    - Drop the Origin Cache Control condition — these headers always
      follow OCC-style semantics regardless of the zone setting.

investigating-uncached-responses.mdx:
- Mirror the same CDN-Cache-Control corrections, framed for the
  troubleshooting reader: note that Cloudflare does not forward
  Cloudflare-CDN-Cache-Control to the client, so a BYPASS without a
  visible CDN-Cache-Control header means checking what the origin
  actually sent (or dropping the header at origin and retesting).
- HIT verification Age bullet: reconcile with the Age scope correction.
  With Tiered Cache, a fresh local fill can already report a large Age
  inherited from an upper tier — not 'may be absent'.
cache-responses.mdx:
- Age callout: qualify the reset rule as 'revalidation against the
  origin'. With Tiered Cache, a lower tier revalidating against an
  upper tier that still has the object picks up the upper tier's age
  rather than resetting to zero.
- CDN-Cache-Control BYPASS bullet: split the sentence on the
  no-cache/max-age=0/s-maxage=0 clarification per style guide (no
  semicolons).

investigating-uncached-responses.mdx:
- CDN-Cache-Control BYPASS bullet: pull the no-cache/max-age=0/
  s-maxage=0 clarification into its own bullet. Six-sentence walls
  are hard to scan while debugging, and the clarification points the
  reader elsewhere (to Repeated MISS), so it earns a separate bullet.
  Also removes the semicolon flagged on the reference page.
- HIT verify Age bullet: restore the absent-on-fill case. On the
  request that fills a lower tier from an upper tier, the client gets
  HIT with no Age at all — the HIT reflects the upper tier, and the
  local data center has not served it from cache yet. The next
  request includes Age and can already be large under Tiered Cache.
investigating-uncached-responses.mdx:
- Name the headers explicitly at the start of the 'no-cache /
  max-age=0 / s-maxage=0' bullet, rather than relying on 'these
  headers'. A scanner reading only the bolded start of BYPASS
  bullets could mis-read a 'these headers' bullet as a BYPASS cause.
- Drop the pointer to 'Repeated MISS'. That section covers cache
  key variance and eviction, which is not what the MISS then
  REVALIDATED / EXPIRED pattern indicates. The bullet already states
  the expected sequence, and 'Confirm the response reaches cache'
  already covers verification.
@ngayerie
ngayerie force-pushed the ngayerie/cache-cdn-cache-control-bypass branch from f2daf62 to 7642ef2 Compare August 20, 2026 16:25
@zaidoon1
zaidoon1 merged commit e620ade into production Aug 21, 2026
16 checks passed
@zaidoon1
zaidoon1 deleted the ngayerie/cache-cdn-cache-control-bypass branch August 21, 2026 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

product:cache Issues or PRs related to Cache size/xs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants