[PR #992] [MERGED] fix(headers): align draft mode cookie attributes with Next.js #1007

Closed
opened 2026-05-06 13:11:31 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/992
Author: @NathanDrake2406
Created: 4/30/2026
Status: Merged
Merged: 4/30/2026
Merged by: @james-elicx

Base: mainHead: nathan/fix-draft-mode-cookie-attrs


📝 Commits (1)

  • 4b1da5d fix(headers): align draft mode cookie attributes with Next.js

📊 Changes

3 files changed (+64 additions, -7 deletions)

View changed files

📝 packages/vinext/src/shims/headers.ts (+10 -6)
📝 tests/nextjs-compat/draft-mode.test.ts (+52 -0)
📝 tests/shims.test.ts (+2 -1)

📄 Description

What this changes

Vinext draft-mode cookies now match Next.js production cookie semantics. draftMode().enable() emits SameSite=None; Secure outside development, and draftMode().disable() clears the bypass cookie with an expired date while preserving the same production attributes.

Why

The existing shim always emitted SameSite=Lax and cleared with Max-Age=0. That diverges from Next.js and can prevent production preview flows from receiving the __prerender_bypass cookie in cross-site contexts, leaving draftMode().isEnabled false even after enabling draft mode.

Next.js references:

  • App Router DraftModeProvider.enable() uses sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax' and secure: process.env.NODE_ENV !== 'development': draft-mode-provider.ts#L68-L75
  • App Router DraftModeProvider.disable() clears via expires: new Date(0) with the same cookie attributes: draft-mode-provider.ts#L80-L92
  • Pages/API preview helpers use the same production sameSite and secure policy: api-resolver.ts#L131-L160

Approach

The draft-mode cookie behavior stays in the normal next/headers shim module. Generated entries still only ask the shim for the pending draft-mode cookie header, so codegen remains app-shape wiring rather than owning cookie policy.

The implementation adds a small helper for draft-mode cookie attributes:

  • development: Path=/; HttpOnly; SameSite=Lax
  • non-development: Path=/; HttpOnly; SameSite=None; Secure

Disable now emits Expires=Thu, 01 Jan 1970 00:00:00 GMT instead of Max-Age=0, matching Next.js's documented source comment and avoiding a cookie-clear form Next.js explicitly does not use.

Validation

  • vp test run tests/nextjs-compat/draft-mode.test.ts -t "cross-site|Next.js production cookie attributes" failed before the fix with SameSite=Lax and Max-Age=0.
  • vp test run tests/nextjs-compat/draft-mode.test.ts
  • vp test run tests/shims.test.ts -t "draftMode"
  • vp lint
  • vp fmt
  • vp check

Risks / follow-ups

This intentionally changes only draft-mode cookie serialization. Request parsing, secret validation, generated entry wiring, and route-handler response plumbing are unchanged.


🔄 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/992 **Author:** [@NathanDrake2406](https://github.com/NathanDrake2406) **Created:** 4/30/2026 **Status:** ✅ Merged **Merged:** 4/30/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `nathan/fix-draft-mode-cookie-attrs` --- ### 📝 Commits (1) - [`4b1da5d`](https://github.com/cloudflare/vinext/commit/4b1da5dd1fece24dffe3bc8884d28f354b292344) fix(headers): align draft mode cookie attributes with Next.js ### 📊 Changes **3 files changed** (+64 additions, -7 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/shims/headers.ts` (+10 -6) 📝 `tests/nextjs-compat/draft-mode.test.ts` (+52 -0) 📝 `tests/shims.test.ts` (+2 -1) </details> ### 📄 Description ## What this changes Vinext draft-mode cookies now match Next.js production cookie semantics. `draftMode().enable()` emits `SameSite=None; Secure` outside development, and `draftMode().disable()` clears the bypass cookie with an expired date while preserving the same production attributes. ## Why The existing shim always emitted `SameSite=Lax` and cleared with `Max-Age=0`. That diverges from Next.js and can prevent production preview flows from receiving the `__prerender_bypass` cookie in cross-site contexts, leaving `draftMode().isEnabled` false even after enabling draft mode. Next.js references: - App Router `DraftModeProvider.enable()` uses `sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax'` and `secure: process.env.NODE_ENV !== 'development'`: [draft-mode-provider.ts#L68-L75](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/packages/next/src/server/async-storage/draft-mode-provider.ts#L68-L75) - App Router `DraftModeProvider.disable()` clears via `expires: new Date(0)` with the same cookie attributes: [draft-mode-provider.ts#L80-L92](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/packages/next/src/server/async-storage/draft-mode-provider.ts#L80-L92) - Pages/API preview helpers use the same production `sameSite` and `secure` policy: [api-resolver.ts#L131-L160](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/packages/next/src/server/api-utils/node/api-resolver.ts#L131-L160) ## Approach The draft-mode cookie behavior stays in the normal `next/headers` shim module. Generated entries still only ask the shim for the pending draft-mode cookie header, so codegen remains app-shape wiring rather than owning cookie policy. The implementation adds a small helper for draft-mode cookie attributes: - development: `Path=/; HttpOnly; SameSite=Lax` - non-development: `Path=/; HttpOnly; SameSite=None; Secure` Disable now emits `Expires=Thu, 01 Jan 1970 00:00:00 GMT` instead of `Max-Age=0`, matching Next.js's documented source comment and avoiding a cookie-clear form Next.js explicitly does not use. ## Validation - `vp test run tests/nextjs-compat/draft-mode.test.ts -t "cross-site|Next.js production cookie attributes"` failed before the fix with `SameSite=Lax` and `Max-Age=0`. - `vp test run tests/nextjs-compat/draft-mode.test.ts` - `vp test run tests/shims.test.ts -t "draftMode"` - `vp lint` - `vp fmt` - `vp check` ## Risks / follow-ups This intentionally changes only draft-mode cookie serialization. Request parsing, secret validation, generated entry wiring, and route-handler response plumbing are unchanged. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:11:31 +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#1007
No description provided.