Skip to content

feat(cloudflare): prewarm canonical RSC navigation requests - #2784

Closed
james-elicx wants to merge 68 commits into
mainfrom
codex/rsc-vary-cache-warm
Closed

feat(cloudflare): prewarm canonical RSC navigation requests#2784
james-elicx wants to merge 68 commits into
mainfrom
codex/rsc-vary-cache-warm

Conversation

@james-elicx

@james-elicx james-elicx commented Jul 31, 2026

Copy link
Copy Markdown
Member

Summary

  • add a provider-neutral CDN-adapter contract for caches that select response variants from Vary verbatim, plus adapter-owned Vary fields that deploy warmers can reproduce; Cloudflare declares X-Forwarded-Proto without putting Cloudflare-specific policy in core vinext
  • use one canonical browser/deploy request for certified cacheable RSC payloads: bare ?_rsc, RSC: 1, Accept: text/x-component, and no source/router-state reuse headers
  • make Link prefetch, router.prefetch(), soft navigation, and deploy prewarming share that identity, including after an intercepted navigation; a completed prefetch is reused instead of issuing a second RSC request
  • retain normal Next-like contextual requests for dynamic, non-cacheable, partial, and intercepted payloads: hashed _rsc, Next-Url, router state, mounted slots, and interception context remain available where response selection needs them
  • certify canonical eligibility from final prerender responses and persist adapter-owned Vary metadata; fail closed for private/no-store responses, cookies, redirects, unsafe Vary, middleware/config effects, or incomplete source-independence proof
  • isolate Workers Cache variants by the RSC semantic headers, credentials, Host, and Cloudflare's overwritten X-Forwarded-Proto; HTML does not vary on Accept
  • use stable __vinext_html / __vinext_rsc family tags across all Vary variants, hosts, and schemes so tag/path invalidation purges the whole representation family as required by Workers Cache
  • warm every concrete configured host while respecting Wrangler route path scopes, environments, workers_dev, basePath, trailingSlash, and deployment ID; wildcard hosts cannot be enumerated and exact non-wildcard routes intentionally cannot receive query-bearing RSC/probe requests
  • stage safe deployments at 0%, verify the staged version through an uncached metadata probe, warm, then promote; bound both response headers and body draining, cancel late/stalled bodies, and retry partial post-promotion failures

Request and behavior implications

  • Cacheable App Router routes intentionally omit Next-Url, Next-Router-State-Tree, prefetch/segment-prefetch, mounted-slot, and interception headers. Their completed payload is proven source-independent, so those headers would only fragment the shared cache.
  • Non-cacheable or context-sensitive routes keep those headers and hashed URLs, preserving source-route/interception behavior instead of forcing unsafe reuse.
  • Cookie and Authorization requests remain no-store/isolated. Host and scheme are explicit Vary dimensions because Workers Cache's base key is path/query/version rather than hostname or protocol.
  • The broader stable family tags mean invalidating any HTML/RSC path or render tag can purge more variants than one concrete request, but avoids leaving stale variants behind when Cloudflare requires identical tags across a Vary family.
  • Background prefetch can wait for eligibility metadata; a user-initiated navigation waits at most 250 ms before falling back to the normal contextual request, so cache certification does not block navigation indefinitely.

Validation

  • focused post-merge cache/RSC/warm/deploy units: 365/365
  • Cloudflare cache/warm/deploy/config suite: 148/148
  • browser reuse suites: 20/20 Link/client-cache tests and 7/7 router-prefetch/interception tests
  • Cloudflare preview smoke: 15/15, including warm canonical HIT -> first browser-shaped HIT and credential/invalid-Accept isolation
  • vp run vinext#build
  • vp run @vinext/cloudflare#build
  • changed-file format, lint, type, shell, knip, and diff checks are clean
  • four fresh specialist reviews plus /bigbonk: no remaining blockers; one timeout/body-drain finding was fixed and independently re-reviewed clean
  • exact-head CI, CodeQL, Deploy Examples, preview smoke, and performance workflows are green

@pkg-pr-new

pkg-pr-new Bot commented Jul 31, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2784
npm i https://pkg.pr.new/create-vinext-app@2784
npm i https://pkg.pr.new/@vinext/types@2784
npm i https://pkg.pr.new/vinext@2784

commit: 586fd81

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
workers-cache preview production
web preview production

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 586fd81 against base 7ed5570 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 3 regressed · 3 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 141.4 KB 143.4 KB ⚫ +1.4%
Client entry size (gzip) vinext 128.8 KB 130.5 KB ⚫ +1.3%
Dev server cold start vinext 3.26 s 3.29 s ⚫ +1.0%
Production build time vinext 3.70 s 3.79 s 🔴 +2.5%
RSC entry closure size (gzip) vinext 115.6 KB 120.8 KB 🔴 +4.5%
Server bundle size (gzip) vinext 196.8 KB 211.1 KB 🔴 +7.2%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

…warm

# Conflicts:
#	tests/cloudflare-cdn-cache.test.ts
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow was cancelled.

View workflow run · To retry, trigger Bonk again.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

Superseded by #3002, an independent implementation based on current main with final-ISR eligibility and deployed browser/cache-reuse coverage.

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