[PR #123] [MERGED] fix(rsc): include cookies in RSC navigation and prefetch requests #330

Closed
opened 2026-05-06 12:39:14 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/123
Author: @SeolJaeHyeok
Created: 2/26/2026
Status: Merged
Merged: 2/27/2026
Merged by: @FredKSchott

Base: mainHead: fix/rsc-navigation-credentials


📝 Commits (1)

  • ae046a7 fix(rsc): include cookies in RSC navigation and prefetch requests

📊 Changes

3 files changed (+3 additions, -0 deletions)

View changed files

📝 packages/vinext/src/server/app-dev-server.ts (+1 -0)
📝 packages/vinext/src/shims/link.tsx (+1 -0)
📝 packages/vinext/src/shims/navigation.ts (+1 -0)

📄 Description

Summary

RSC navigation and prefetch requests were missing credentials: 'include',
causing browsers to silently omit cookies from these requests. This broke
cookie-based authentication (Better Auth, NextAuth, etc.) during client-side
navigation.

Root cause: A full-page reload works correctly — the browser always sends
cookies with HTML navigation requests. But when a user clicks a <Link>, vinext
fetches the RSC payload via fetch(), and without credentials: 'include' the
browser omits all cookies from the request. The server receives no session cookie
and authentication fails.

Three call sites fixed:

File Context
server/app-dev-server.ts __VINEXT_RSC_NAVIGATE__ — actual navigation fetch
shims/link.tsx <Link> prefetch triggered on viewport intersection
shims/navigation.ts router.prefetch() programmatic prefetch
- navResponse = await fetch(rscUrl, {
-   headers: { Accept: "text/x-component" },
- });
+ navResponse = await fetch(rscUrl, {
+   headers: { Accept: "text/x-component" },
+   credentials: "include",
+ });

Reproduction

  1. Set up cookie-based auth (e.g., Better Auth or NextAuth)
  2. Have a protected route that calls cookies() on the server
  3. Navigate to a public page, then click a to the protected route
  4. Before: Auth fails — session cookie not sent with the RSC fetch
  5. After: Auth succeeds — cookies included in all RSC requests

A full page refresh on the protected route always worked correctly. Only
client-side navigation was affected.

Notes

  • credentials: 'include' is the correct choice here: RSC fetches are always
    same-origin (they go to the same vinext dev server / production host), so this
    cannot cause credential leakage to third parties. Rewrites to external origins
    are handled by the server-side proxy which already strips cookies before
    forwarding.
  • Prefetch requests with stale/unauthorised responses are discarded when
    consumed — no change to that logic.

Fixes #85


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/123 **Author:** [@SeolJaeHyeok](https://github.com/SeolJaeHyeok) **Created:** 2/26/2026 **Status:** ✅ Merged **Merged:** 2/27/2026 **Merged by:** [@FredKSchott](https://github.com/FredKSchott) **Base:** `main` ← **Head:** `fix/rsc-navigation-credentials` --- ### 📝 Commits (1) - [`ae046a7`](https://github.com/cloudflare/vinext/commit/ae046a7e889a105a9fecaf2babff4571304081e0) fix(rsc): include cookies in RSC navigation and prefetch requests ### 📊 Changes **3 files changed** (+3 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/server/app-dev-server.ts` (+1 -0) 📝 `packages/vinext/src/shims/link.tsx` (+1 -0) 📝 `packages/vinext/src/shims/navigation.ts` (+1 -0) </details> ### 📄 Description ## Summary RSC navigation and prefetch requests were missing `credentials: 'include'`, causing browsers to silently omit cookies from these requests. This broke cookie-based authentication (Better Auth, NextAuth, etc.) during client-side navigation. **Root cause:** A full-page reload works correctly — the browser always sends cookies with HTML navigation requests. But when a user clicks a `<Link>`, vinext fetches the RSC payload via `fetch()`, and without `credentials: 'include'` the browser omits all cookies from the request. The server receives no session cookie and authentication fails. **Three call sites fixed:** | File | Context | |---|---| | `server/app-dev-server.ts` | `__VINEXT_RSC_NAVIGATE__` — actual navigation fetch | | `shims/link.tsx` | `<Link>` prefetch triggered on viewport intersection | | `shims/navigation.ts` | `router.prefetch()` programmatic prefetch | ```diff - navResponse = await fetch(rscUrl, { - headers: { Accept: "text/x-component" }, - }); + navResponse = await fetch(rscUrl, { + headers: { Accept: "text/x-component" }, + credentials: "include", + }); ``` Reproduction 1. Set up cookie-based auth (e.g., Better Auth or NextAuth) 2. Have a protected route that calls cookies() on the server 3. Navigate to a public page, then click a <Link> to the protected route 4. Before: Auth fails — session cookie not sent with the RSC fetch 5. After: Auth succeeds — cookies included in all RSC requests A full page refresh on the protected route always worked correctly. Only client-side <Link> navigation was affected. Notes - credentials: 'include' is the correct choice here: RSC fetches are always same-origin (they go to the same vinext dev server / production host), so this cannot cause credential leakage to third parties. Rewrites to external origins are handled by the server-side proxy which already strips cookies before forwarding. - Prefetch requests with stale/unauthorised responses are discarded when consumed — no change to that logic. Fixes #85 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 12:39:14 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#330
No description provided.