Skip to content

feat(matcher): make matcher-wrapped pages cacheable at CDN edge#1203

Merged
vibe-dex merged 1 commit into
mainfrom
vibe-dex/cache-vary-cookie-safety-stable
May 27, 2026
Merged

feat(matcher): make matcher-wrapped pages cacheable at CDN edge#1203
vibe-dex merged 1 commit into
mainfrom
vibe-dex/cache-vary-cookie-safety-stable

Conversation

@vibe-dex

@vibe-dex vibe-dex commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Drops Vary: cookie from sticky-session matchers (blocks/matcher.ts). The cookie value determines the variant; CDN cache keys target the specific cookie name, so Vary: cookie is overbroad and forces every CDN to bypass cache for matcher pages.
  • Filters framework-managed Set-Cookies (deco_matcher_*, deco_segment) out of the kill-switch in runtime/middleware.ts. Only foreign Set-Cookies (cart, profile, etc.) downgrade Cache-Control to no-store.
  • Wires ctx.var.vary.shouldCache into the full-page kill-switch. Loaders that declare cache: "no-store" (the documented contract for personalizing loaders) now veto full-page caching the same way they already veto partial-section caching at runtime/routes/render.tsx:118-134.
  • Emits a Deco-Cache-Vary-Cookies hint header listing the framework cookies present, so CDN operators can discover which cookies belong in the custom cache key.
  • Extracts the kill-switch logic into an exported pure applyPageCacheDecision(headers, input) for testability.
  • Adds first-ever tests for blocks/matcher.ts and runtime/middleware.ts.

Why

Customer Lojas Torra (~9% of fleet egress, ~5 TB/month) was 100% origin-bound on PDP/PLP/search/home despite a 70-83% overall hit rate. Every matcher-wrapped page emitted Vary: cookie + Cache-Control: no-store, making the response uncacheable at any CDN regardless of cache rules. This is a fleet-wide issue — any deco-cx/deco site with active matchers (A/B tests, banners, sticky variants) has uncacheable pages.

Validation on @deco/deco@1.200.1-next.2

Already prereleased as jsr:@deco/deco@1.200.1-next.2 (tag points to a separate branch with the same single commit + a prerelease version bump — kept off this PR to keep the diff clean for the bot's auto-bump).

Verified on Lojas Torra: matcher-wrapped pages now emit Vary: Accept-Encoding + Cache-Control: public, max-age=90, swr=3600, sie=86400 + Deco-Cache-Vary-Cookies: deco_matcher_<hash>, deco_segment. CDN cache hit rate moved as expected once the Cloudflare cache rule was re-enabled. No cross-user leakage observed.

Decision table (new)

Condition Cache-Control
Any foreign Set-Cookie (cart, profile, etc.) no-store, no-cache, must-revalidate
Any loader marked cache: "no-store" (vary.shouldCache===false) no-store, no-cache, must-revalidate
Any flag with cacheable: false no-store, no-cache, must-revalidate
Matcher-only page, all loaders cacheable, HTML, PAGE_CACHE_ALLOWED_KEY set PAGE_CACHE_CONTROL + Deco-Cache-Vary-Cookies hint
Anything else headers untouched

Safety / sequencing

This relies on the loader-level cache: "no-store" contract being respected for personalizing loaders. Companion work in deco-cx/apps adds the declaration to 24 loaders that personalize SSR but didn't declare it (VTEX/Linx/Shopify/WAP/Vnda/Nuvemshop). That PR is being prepared in parallel and should be released before broader rollout. Wake was already correct (declares no-store on user/cart/wishlist; partner-token loaders use null cacheKey which already flips vary.shouldCache=false).

Test plan

  • deno test --unstable-http -A blocks/matcher.test.ts runtime/middleware.test.ts — 12 new tests pass
  • deno test --unstable-http -A runtime/caches/ engine/ clients/ utils/ — 26 pre-existing tests still pass
  • deno check live.ts clean
  • 1.200.1-next.2 prerelease published to JSR; published from a clean main-based branch (the original PR feat(matcher): make matcher-wrapped pages cacheable at CDN edge #1202 merged into a polluted next branch and was reverted)
  • End-to-end verified on Lojas Torra (www.lojastorra.com.br) with the prerelease — cache HITs, no cross-user leakage
  • Companion @deco/apps PR adding cache: "no-store" to 24 personalizing loaders (in progress)

🤖 Generated with Claude Code


Summary by cubic

Make matcher-wrapped pages cacheable at the CDN edge by removing Vary: cookie and only disabling caching when foreign cookies or personalizing loaders are present. Adds a hint header so CDNs can include framework cookies in the cache key.

  • New Features

    • Dropped Vary: cookie from sticky matchers; framework cookies (deco_matcher_*, deco_segment) no longer force no-store.
    • Respects ctx.var.vary.shouldCache; loaders with cache: "no-store" veto full-page cache.
    • Emits Deco-Cache-Vary-Cookies listing framework cookies for CDN cache-key rules.
  • Refactors

    • Extracted applyPageCacheDecision(headers, input); added tests for blocks/matcher.ts and runtime/middleware.ts.
    • Page cache applies only to HTML with PAGE_CACHE_ALLOWED_KEY; foreign Set-Cookie or cacheable: false flags set no-store.

Written for commit ab02230. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Removed unnecessary Vary: cookie header from sticky-matcher cookie responses, improving cache efficiency.
  • Improvements

    • Enhanced page cache decision logic to intelligently manage cache control headers based on cookie presence and cache eligibility, preventing cache issues with framework-specific cookies.

Review Change Stack

Drop `Vary: cookie` from sticky-session matchers and stop treating
framework-managed Set-Cookies (deco_matcher_*, deco_segment) as a
reason to disable caching. Wire ctx.var.vary.shouldCache into the
full-page kill-switch so loaders that declare cache:"no-store"
(personalizing loaders) still veto caching.

Adds a Deco-Cache-Vary-Cookies hint header so CDN operators can
discover which cookies belong in the custom cache key.

Extracts applyPageCacheDecision() from the inlined kill-switch as
an exported pure function; the request middleware is the only
production caller. Adds tests for matcher.ts and middleware.ts —
the first tests in blocks/ and for runtime/middleware.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Tagging Options

Should a new tag be published when this PR is merged?

  • 👍 for Patch 1.200.1 update
  • 🎉 for Minor 1.201.0 update
  • 🚀 for Major 2.0.0 update

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Sticky matcher no longer emits Vary: cookie headers. A new applyPageCacheDecision utility centralizes page-cache header logic, detecting foreign cookies and flag cacheability to determine Cache-Control and Deco-Cache-Vary-Cookies headers. Middleware is refactored to use this utility.

Changes

Sticky matcher and page cache decision refactoring

Layer / File(s) Summary
Sticky matcher cookie behavior without Vary header
blocks/matcher.ts, blocks/matcher.test.ts
Sticky matcher no longer appends Vary: cookie when writing matcher cookies. Tests validate that sticky matchers emit Set-Cookie without Vary: cookie, pre-existing matching cookies do not trigger new headers, non-sticky matchers leave headers untouched, and a regression test confirms the source change.
Page cache decision utility and cookie-aware caching rules
runtime/middleware.ts, runtime/middleware.test.ts
New exported PageCacheDecisionInput type and applyPageCacheDecision function. Detects "foreign" Set-Cookie headers and forces Cache-Control: no-store/no-cache/must-revalidate if present, or if caching is disqualified by flags or shouldCacheFromVary. When caching is allowed, sets default Cache-Control: public and emits Deco-Cache-Vary-Cookies listing framework cookie names. Tests cover cookie, flag, and cacheability conditions, plus preservation and override of pre-existing Cache-Control.
Middleware integration of page cache decision utility
runtime/middleware.ts
Imports matcher constants DECO_MATCHER_HEADER_QS and DECO_MATCHER_PREFIX. Middleware computes isPageCacheAllowed from page-cache allow flag and HTML content-type, then delegates cache-header mutation to applyPageCacheDecision, replacing previous inline branching.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • deco-cx/deco#1200: Modifies runtime/middleware.ts page-caching eligibility to become opt-in via PAGE_CACHE_ALLOWED_KEY, with main PR further refactoring that logic into applyPageCacheDecision.
  • deco-cx/deco#1201: Changes the specific DECO_PAGE_CACHE_CONTROL/PAGE_CACHE_CONTROL value used in cache-control defaults that the main PR's refactoring now sets.
  • deco-cx/deco#1123: Modifies blocks/matcher.ts sticky/cookie decision logic and the matcher path that mutates response Vary with "cookie", which directly overlaps with main PR's removal of that header.

Suggested reviewers

  • guitavano
  • igoramf

Poem

🐰 No more Vary: cookie in the matcher's sweet treat,
A new caching decision makes the logic complete,
Foreign cookies trigger no-store, what a sight,
Framework cookies tracked in headers, burning bright!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the changeset: enabling CDN caching for matcher-wrapped pages by removing cache-blocking headers and refining cookie handling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vibe-dex/cache-vary-cookie-safety-stable

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vibe-dex
vibe-dex merged commit df9116c into main May 27, 2026
5 of 8 checks passed
@vibe-dex
vibe-dex deleted the vibe-dex/cache-vary-cookie-safety-stable branch May 27, 2026 04:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant