Build-time prerender seeding (`isrSetPrerenderedAppPage`) and TPR upload were
writing cache entries with `tags: []`. Tag-based invalidation could never
reach them, so `revalidatePath('/foo')` left the seeded HTML/RSC stale until
the natural `revalidateAt` expired.
Compute the path-derived implicit tags via `buildAppPageCacheTags(pathname, [])`
in both seed paths and forward them through to the cache handler.
Closes (in part) #1486 — plain path invalidation. Rewrite-aware
invalidation (canonical-vs-rewrite key mismatch) is left for a follow-up.
* fix(app-router): preserve encoded prerender params
App Router prerendered pages with encoded dynamic params could be bypassed in production and rendered again with decoded route params. This diverged from Next.js for generateStaticParams values like "sticks & stones".
The root cause was split across build and startup. The prerender render path only had the encoded URL, so normal route matching decoded the page params. After preserving encoded params for the render, production startup still seeded the ISR cache under the encoded artifact path while live requests lookup cache entries by the runtime-normalized pathname.
Carry trusted prerender params through the prerender-only request path, keep decoded params for dynamicParams validation, and seed prerendered App Router artifacts under the normalized runtime cache key while reading files from their encoded output path.
* chore(app-router): clean up prerender header internals
Reuse the shared record guard for prerender route param parsing and remove the local duplicate.
Keep Next.js INTERNAL_HEADERS as the exact upstream list, with vinext-only internal headers tracked separately but stripped by the same request pipeline. This fixes the CI unit failure while preserving the prerender header trust boundary.
Factor repeated Node-header conversion and forwarded-protocol handling in the production server to avoid local assertions and duplicate loops.
* fix(app-router): scope prerender params to route pattern
Bind the internal prerender route-param header to the route pattern that produced it so encoded render params cannot cross a rewrite boundary into a different dynamic route shape.
The App Router handler now only applies encoded prerender params when the payload routePattern matches the final matched route. Otherwise it falls back to the decoded params from normal route matching.
Tighten the header parser to validate the payload shape with Object.entries/Object.keys and add a regression for a prerendered source route rewritten to a dynamic target route with a different param name.
* fix(app-router): verify prerender params match route
Require trusted prerender route params to prove both the final route pattern and the final decoded matched params before using their encoded render values.
This prevents same-pattern rewrites from carrying stale prerender params into a different concrete route match, while keeping valid prerender renders encoded and static validation decoded.
Add handler regression coverage for same-pattern rewrites and helper coverage for catch-all array comparison.
* docs(app-router): clarify prerender route-param trust boundary
The prerender route-param readers and serializer carried implicit
coupling that read as security verification but actually delegated it.
A reader new to readTrustedPrerenderRouteParams could assume it verifies
the prerender secret, when verification happens once upstream at
prod-server's nodeToWebRequest boundary and every downstream read trusts
that. The double read-then-reattach in createAppRscHandler also depends
on the secret header surviving filterInternalHeaders, with no note of
that coupling.
Document the trust contract: secret is verified at the outer boundary,
downstream reads operate on already-trusted requests gated by
VINEXT_PRERENDER=1, and the secret header must stay out of the internal
strip list for the second read to succeed. Also document the
empty-params to null serialization contract.
No behavior change; comments only.
* feat: seed memory cache from pre-rendered routes at startup
Read vinext-prerender.json and pre-rendered HTML/RSC files at production
server startup to populate the MemoryCacheHandler. This ensures the first
request to any pre-rendered page is a cache HIT instead of triggering a
full re-render.
- Add seedMemoryCacheFromPrerender() in server/seed-cache.ts
- Add buildId, router, and trailingSlash to the prerender manifest
- Call seeding during App Router production server startup
- 11 unit tests covering ISR routes, static routes, dynamic routes,
trailingSlash, graceful degradation, and missing files
Closes#561
* fix: address review feedback on cache seeding PR
- Warn on corrupt manifest JSON instead of silent swallow
- Always serialize trailingSlash (typeof boolean, not truthiness)
- Populate setRevalidateDuration() for seeded entries so
Cache-Control headers are correct on first HIT
- Return seeded route count and log it at startup
- Add tests: corrupt JSON, missing buildId, FNV hash path,
revalidate duration tracking, return value assertions
Addresses review comments on #645.