From 5a75a3111fd14ec37c42a415d480514498b71643 Mon Sep 17 00:00:00 2001 From: magnus Date: Sun, 4 May 2025 00:50:03 +0200 Subject: [PATCH 1/4] fix cookie set in middleware on initial render --- packages/open-next/src/core/requestHandler.ts | 2 ++ packages/open-next/src/core/routing/middleware.ts | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/open-next/src/core/requestHandler.ts b/packages/open-next/src/core/requestHandler.ts index 26f8df33..a45a3e41 100644 --- a/packages/open-next/src/core/requestHandler.ts +++ b/packages/open-next/src/core/requestHandler.ts @@ -92,6 +92,8 @@ export async function openNextHandler( delete headers[rawKey]; } + delete overwrittenResponseHeaders["x-middleware-set-cookie"]; + if ( "isExternalRewrite" in routingResult && routingResult.isExternalRewrite === true diff --git a/packages/open-next/src/core/routing/middleware.ts b/packages/open-next/src/core/routing/middleware.ts index cd4c4762..2d52e6e8 100644 --- a/packages/open-next/src/core/routing/middleware.ts +++ b/packages/open-next/src/core/routing/middleware.ts @@ -115,7 +115,6 @@ export async function handleMiddleware( // These are internal headers used by Next.js, we don't want to expose them to the client const filteredHeaders = [ "x-middleware-override-headers", - "x-middleware-set-cookie", "x-middleware-next", "x-middleware-rewrite", // We need to drop `content-encoding` because it will be decoded From 1b6eba225a6026eac5cbe926c5a34a7575e50d04 Mon Sep 17 00:00:00 2001 From: magnus Date: Sun, 4 May 2025 01:04:08 +0200 Subject: [PATCH 2/4] e2e --- .changeset/breezy-adults-behave.md | 5 ++++ examples/app-router/app/cookies/page.tsx | 7 +++++ examples/app-router/middleware.ts | 5 ++++ .../appRouter/middleware.cookies.test.ts | 26 ++++++++++++++----- 4 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 .changeset/breezy-adults-behave.md create mode 100644 examples/app-router/app/cookies/page.tsx diff --git a/.changeset/breezy-adults-behave.md b/.changeset/breezy-adults-behave.md new file mode 100644 index 00000000..00c02ebe --- /dev/null +++ b/.changeset/breezy-adults-behave.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": patch +--- + +fix: Ensure cookies set in middleware are available on initial render when using `cookies().get()` from Next.js diff --git a/examples/app-router/app/cookies/page.tsx b/examples/app-router/app/cookies/page.tsx new file mode 100644 index 00000000..60ef69a7 --- /dev/null +++ b/examples/app-router/app/cookies/page.tsx @@ -0,0 +1,7 @@ +import { cookies } from "next/headers"; + +export default async function Page() { + const foo = (await cookies()).get("foo")?.value; + + return
{foo}
; +} diff --git a/examples/app-router/middleware.ts b/examples/app-router/middleware.ts index 88a6c140..a634eb4a 100644 --- a/examples/app-router/middleware.ts +++ b/examples/app-router/middleware.ts @@ -28,6 +28,11 @@ export function middleware(request: NextRequest) { const u = new URL("https://opennext.js.org/share.png"); return NextResponse.rewrite(u); } + if (path === "/cookies") { + const res = NextResponse.next(); + res.cookies.set("foo", "bar"); + return res; + } const requestHeaders = new Headers(request.headers); // Setting the Request Headers, this should be available in RSC requestHeaders.set("request-header", "request-header"); diff --git a/packages/tests-e2e/tests/appRouter/middleware.cookies.test.ts b/packages/tests-e2e/tests/appRouter/middleware.cookies.test.ts index 9c92fe60..23512625 100644 --- a/packages/tests-e2e/tests/appRouter/middleware.cookies.test.ts +++ b/packages/tests-e2e/tests/appRouter/middleware.cookies.test.ts @@ -1,12 +1,24 @@ import { expect, test } from "@playwright/test"; -test("Cookies", async ({ page, context }) => { - await page.goto("/"); +test.describe("Middleware Cookies", () => { + test("should be able to set cookies on response in middleware", async ({ + page, + context, + }) => { + await page.goto("/"); - const cookies = await context.cookies(); - const from = cookies.find(({ name }) => name === "from"); - expect(from?.value).toEqual("middleware"); + const cookies = await context.cookies(); + const from = cookies.find(({ name }) => name === "from"); + expect(from?.value).toEqual("middleware"); - const love = cookies.find(({ name }) => name === "with"); - expect(love?.value).toEqual("love"); + const love = cookies.find(({ name }) => name === "with"); + expect(love?.value).toEqual("love"); + }); + test("should be able to get cookies set in the middleware with Next's cookies().get()", async ({ + page, + }) => { + await page.goto("/cookies"); + + expect(await page.getByTestId("foo").textContent()).toBe("bar"); + }); }); From a3b374d7dec5315e8cb0a6c948e844a314d255ee Mon Sep 17 00:00:00 2001 From: magnus Date: Sun, 4 May 2025 09:29:10 +0200 Subject: [PATCH 3/4] add another test for internal next headers --- .../appRouter/middleware.cookies.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/tests-e2e/tests/appRouter/middleware.cookies.test.ts b/packages/tests-e2e/tests/appRouter/middleware.cookies.test.ts index 23512625..e35324a1 100644 --- a/packages/tests-e2e/tests/appRouter/middleware.cookies.test.ts +++ b/packages/tests-e2e/tests/appRouter/middleware.cookies.test.ts @@ -21,4 +21,24 @@ test.describe("Middleware Cookies", () => { expect(await page.getByTestId("foo").textContent()).toBe("bar"); }); + test("should not expose internal Next headers in response", async ({ + page, + context, + }) => { + const responsePromise = page.waitForResponse((response) => + response.url().includes("/cookies"), + ); + + await page.goto("/cookies"); + + const response = await responsePromise; + const headers = response.headers(); + + const cookies = await context.cookies(); + const fooCookie = cookies.find(({ name }) => name === "foo"); + expect(fooCookie?.value).toBe("bar"); + + expect(headers).not.toHaveProperty("x-middleware-set-cookie"); + expect(headers).not.toHaveProperty("x-middleware-next"); + }); }); From a1a35044c4225db53356d98d864d55fd9cf86bd5 Mon Sep 17 00:00:00 2001 From: magnus Date: Sun, 4 May 2025 17:48:57 +0200 Subject: [PATCH 4/4] refactor --- packages/open-next/src/core/requestHandler.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/open-next/src/core/requestHandler.ts b/packages/open-next/src/core/requestHandler.ts index a45a3e41..1a0e0917 100644 --- a/packages/open-next/src/core/requestHandler.ts +++ b/packages/open-next/src/core/requestHandler.ts @@ -87,13 +87,15 @@ export async function openNextHandler( continue; } const key = rawKey.slice(MIDDLEWARE_HEADER_PREFIX_LEN); - overwrittenResponseHeaders[key] = value; + // We skip this header here since it is used by Next internally and we don't want it on the response headers. + // This header needs to be present in the request headers for processRequest, so cookies().get() from Next will work on initial render. + if (key !== "x-middleware-set-cookie") { + overwrittenResponseHeaders[key] = value; + } headers[key] = value; delete headers[rawKey]; } - delete overwrittenResponseHeaders["x-middleware-set-cookie"]; - if ( "isExternalRewrite" in routingResult && routingResult.isExternalRewrite === true