Skip to content

feat(website): AllowPageCache section for CMS-only pages - #1649

Closed
igoramf wants to merge 1 commit into
mainfrom
feat/website-allow-page-cache-section
Closed

feat(website): AllowPageCache section for CMS-only pages#1649
igoramf wants to merge 1 commit into
mainfrom
feat/website-allow-page-cache-section

Conversation

@igoramf

@igoramf igoramf commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Problem

The FARM home (and any CMS-only page) isn't being CDN-cached because it doesn't go through any VTEX loader.

The runtime (deco/runtime/middleware.tsapplyPageCacheDecision) uses an opt-in model: it only emits a public Cache-Control when some middleware has set PAGE_CACHE_ALLOWED_KEY on the request bag.

The only thing that sets that key is apps/vtex/middleware.ts. But an app middleware is composed as a resolver middleware (blocks/appsUtil.tscompose(...middlewares, blockResolver)), so it only runs when a block from that app is resolved. A page built purely from website/CMS sections with no commerce loader never triggers it → isPageCacheAllowed = false → no public Cache-Control → the page is never cached.

Fix

An editor-droppable website section that opts the current page into the existing caching pipeline by setting PAGE_CACHE_ALLOWED_KEY from its loader. It renders nothing.

export const loader = (_props, _req, ctx: AppContext) => {
  ctx.bag?.set(PAGE_CACHE_ALLOWED_KEY, true);
  return {};
};

Drop Allow Page Cache on a CMS-only page (e.g. the home) to make it cacheable.

Why it's safe

It's still fully guarded by the runtime applyPageCacheDecision:

  • a foreign (non-framework) Set-Cookieno-store wins;
  • shouldCacheFromVary === falseno-store wins;
  • any block that already set Cache-Control (e.g. a VTEX loader marking no-store for a logged-in/segmented request) wins, since the runtime only sets the public directive if (!headers.has("Cache-Control")).

So it never forces caching of a personalized response — it only fills the opt-in gap for pages that have no app to vouch for them.

Notes

  • Uses the same ctx.bag.set(PAGE_CACHE_ALLOWED_KEY, true) API as the VTEX middleware.
  • Registered in website/manifest.gen.ts.
  • deno check website/mod.ts passes; deno fmt applied.

🤖 Generated with Claude Code


Summary by cubic

Adds a new AllowPageCache website section that lets CMS‑only pages opt into CDN caching by setting PAGE_CACHE_ALLOWED_KEY. It renders nothing and respects existing runtime cache safety checks.

  • New Features

    • Droppable section that sets PAGE_CACHE_ALLOWED_KEY in its loader to enable public Cache-Control on CMS-only pages (e.g., home).
    • Safe by design: runtime still prefers no-store when Set-Cookie is present or another block sets cache headers.
  • Migration

    • Add AllowPageCache to any CMS-only page you want cached; no props needed.

Written for commit 4580918. Summary will update on new commits.

Review in cubic

The runtime only emits a public Cache-Control when a middleware marks the
request as cacheable via PAGE_CACHE_ALLOWED_KEY. Commerce apps (VTEX) set this
from their middleware, but that middleware is a resolver middleware and only
runs when a block from that app is resolved. CMS-only pages (e.g. a home built
purely from website sections, no commerce loader) never opt in and stay
uncached.

This adds an editor-droppable section that opts the current page into caching
by setting the bag key. It renders nothing and remains guarded by the runtime:
a foreign Set-Cookie or a no-store from another block on the page still wins.

Co-Authored-By: Claude Opus 4.8 <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 0.160.1 update
  • 🎉 for Minor 0.161.0 update
  • 🚀 for Major 1.0.0 update

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a non-visual AllowPageCache section whose loader sets the page-cache opt-in flag, then registers the section in the generated website manifest.

Changes

Page cache opt-in

Layer / File(s) Summary
AllowPageCache loader
website/sections/Cache/AllowPageCache.tsx
Adds a loader that sets PAGE_CACHE_ALLOWED_KEY in ctx.bag and a default component that renders null.
Manifest registration
website/manifest.gen.ts
Imports AllowPageCache and adds it to the manifest’s sections mapping.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • deco-cx/apps#1597: Updates VTEX middleware caching logic around the same PAGE_CACHE_ALLOWED_KEY mechanism.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change, but it does not follow the required template and omits the issue link, Loom video, and demonstration link. Reformat it to the repo template and add the required Issue Link, Loom Video, and Demonstration Link sections, plus a brief contribution summary.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title clearly names the new AllowPageCache website section and its CMS-only caching purpose.
✨ 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 feat/website-allow-page-cache-section

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="website/sections/Cache/AllowPageCache.tsx">

<violation number="1" location="website/sections/Cache/AllowPageCache.tsx:18">
P2: The loader uses `ctx.bag?.set(...)`, which will silently do nothing if `bag` is undefined — but setting this key is the sole purpose of the section, so a silent no-op means the page stays uncached with no indication why. The VTEX middleware this PR mirrors uses non-optional `ctx.bag.set(...)`; consider matching that or adding a warning if bag is missing.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

* same page marks the response `no-store`, that decision wins.
*/
export const loader = (_props: unknown, _req: Request, ctx: AppContext) => {
ctx.bag?.set(PAGE_CACHE_ALLOWED_KEY, true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The loader uses ctx.bag?.set(...), which will silently do nothing if bag is undefined — but setting this key is the sole purpose of the section, so a silent no-op means the page stays uncached with no indication why. The VTEX middleware this PR mirrors uses non-optional ctx.bag.set(...); consider matching that or adding a warning if bag is missing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At website/sections/Cache/AllowPageCache.tsx, line 18:

<comment>The loader uses `ctx.bag?.set(...)`, which will silently do nothing if `bag` is undefined — but setting this key is the sole purpose of the section, so a silent no-op means the page stays uncached with no indication why. The VTEX middleware this PR mirrors uses non-optional `ctx.bag.set(...)`; consider matching that or adding a warning if bag is missing.</comment>

<file context>
@@ -0,0 +1,25 @@
+ * same page marks the response `no-store`, that decision wins.
+ */
+export const loader = (_props: unknown, _req: Request, ctx: AppContext) => {
+  ctx.bag?.set(PAGE_CACHE_ALLOWED_KEY, true);
+  return {};
+};
</file context>

@igoramf

igoramf commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by the cacheable prop on the Page block (better UX than an invisible no-render section, no manifest churn, same PAGE_CACHE_ALLOWED_KEY mechanism/safety). See the new PR.

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