feat(matcher): make matcher-wrapped pages cacheable at CDN edge#1203
Conversation
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>
Tagging OptionsShould a new tag be published when this PR is merged?
|
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughSticky matcher no longer emits ChangesSticky matcher and page cache decision refactoring
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Summary
Vary: cookiefrom sticky-session matchers (blocks/matcher.ts). The cookie value determines the variant; CDN cache keys target the specific cookie name, soVary: cookieis overbroad and forces every CDN to bypass cache for matcher pages.deco_matcher_*,deco_segment) out of the kill-switch inruntime/middleware.ts. Only foreign Set-Cookies (cart, profile, etc.) downgradeCache-Controltono-store.ctx.var.vary.shouldCacheinto the full-page kill-switch. Loaders that declarecache: "no-store"(the documented contract for personalizing loaders) now veto full-page caching the same way they already veto partial-section caching atruntime/routes/render.tsx:118-134.Deco-Cache-Vary-Cookieshint header listing the framework cookies present, so CDN operators can discover which cookies belong in the custom cache key.applyPageCacheDecision(headers, input)for testability.blocks/matcher.tsandruntime/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.2Already 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)
no-store, no-cache, must-revalidatecache: "no-store"(vary.shouldCache===false)no-store, no-cache, must-revalidatecacheable: falseno-store, no-cache, must-revalidatePAGE_CACHE_CONTROL+Deco-Cache-Vary-CookieshintSafety / sequencing
This relies on the loader-level
cache: "no-store"contract being respected for personalizing loaders. Companion work indeco-cx/appsadds 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 (declaresno-storeon user/cart/wishlist; partner-token loaders use null cacheKey which already flipsvary.shouldCache=false).Test plan
deno test --unstable-http -A blocks/matcher.test.ts runtime/middleware.test.ts— 12 new tests passdeno test --unstable-http -A runtime/caches/ engine/ clients/ utils/— 26 pre-existing tests still passdeno check live.tsclean1.200.1-next.2prerelease 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 pollutednextbranch and was reverted)www.lojastorra.com.br) with the prerelease — cache HITs, no cross-user leakage@deco/appsPR addingcache: "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: cookieand 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
Vary: cookiefrom sticky matchers; framework cookies (deco_matcher_*,deco_segment) no longer forceno-store.ctx.var.vary.shouldCache; loaders withcache: "no-store"veto full-page cache.Deco-Cache-Vary-Cookieslisting framework cookies for CDN cache-key rules.Refactors
applyPageCacheDecision(headers, input); added tests forblocks/matcher.tsandruntime/middleware.ts.PAGE_CACHE_ALLOWED_KEY; foreignSet-Cookieorcacheable: falseflags setno-store.Written for commit ab02230. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
Bug Fixes
Vary: cookieheader from sticky-matcher cookie responses, improving cache efficiency.Improvements