From b290a909852cc33267664896c3da0748e8b3b321 Mon Sep 17 00:00:00 2001 From: Dirk de Visser Date: Fri, 12 Sep 2025 15:28:48 +0200 Subject: [PATCH 1/5] feat: add option to align header priorization execution order Fixes opennextjs/opennextjs-aws#973 --- .../app/headers/execution-order/page.tsx | 9 +++++++++ examples/app-router/middleware.ts | 5 +++++ examples/app-router/open-next.config.ts | 3 +++ packages/open-next/src/core/routingHandler.ts | 20 +++++++++++++++---- packages/open-next/src/types/open-next.ts | 10 ++++++++++ .../tests-e2e/tests/appRouter/headers.test.ts | 17 ++++++++++++++++ 6 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 examples/app-router/app/headers/execution-order/page.tsx diff --git a/examples/app-router/app/headers/execution-order/page.tsx b/examples/app-router/app/headers/execution-order/page.tsx new file mode 100644 index 000000000..cf041579c --- /dev/null +++ b/examples/app-router/app/headers/execution-order/page.tsx @@ -0,0 +1,9 @@ +export default async function HeadersExecutionOrder() { + return ( +
+

+ Used to check execution order of middleware vs next.config headers. +

+
+ ); +} diff --git a/examples/app-router/middleware.ts b/examples/app-router/middleware.ts index e5da8f98c..3e472d45b 100644 --- a/examples/app-router/middleware.ts +++ b/examples/app-router/middleware.ts @@ -50,6 +50,11 @@ export function middleware(request: NextRequest) { // Response headers should show up in the client's response headers responseHeaders.set("response-header", "response-header"); + // For dangerous.middlewareHeadersOverrideNextConfigHeaders we need to verify that middleware headers override next.config.js headers. + if (path === "/headers/execution-order") { + responseHeaders.set("e2e-headers", "middleware"); + } + // Set the cache control header with custom swr // For: isr.test.ts if (path === "/isr" && !request.headers.get("x-prerender-revalidate")) { diff --git a/examples/app-router/open-next.config.ts b/examples/app-router/open-next.config.ts index 5ff6edf53..bc9430bb8 100644 --- a/examples/app-router/open-next.config.ts +++ b/examples/app-router/open-next.config.ts @@ -8,6 +8,9 @@ const config = { }, }, functions: {}, + dangerous: { + middlewareHeadersOverrideNextConfigHeaders: true, + }, buildCommand: "npx turbo build", }; diff --git a/packages/open-next/src/core/routingHandler.ts b/packages/open-next/src/core/routingHandler.ts index 8a70076ad..951be07c5 100644 --- a/packages/open-next/src/core/routingHandler.ts +++ b/packages/open-next/src/core/routingHandler.ts @@ -129,10 +129,22 @@ export default async function routingHandler( return middlewareEventOrResult; } - headers = { - ...middlewareEventOrResult.responseHeaders, - ...headers, - }; + const middlewareHeadersPrioritized = + globalThis.openNextConfig.dangerous + ?.middlewareHeadersOverrideNextConfigHeaders ?? false; + + if (middlewareHeadersPrioritized) { + headers = { + ...headers, + ...middlewareEventOrResult.responseHeaders, + }; + } else { + headers = { + ...middlewareEventOrResult.responseHeaders, + ...headers, + }; + } + let isExternalRewrite = middlewareEventOrResult.isExternalRewrite ?? false; eventOrResult = middlewareEventOrResult; diff --git a/packages/open-next/src/types/open-next.ts b/packages/open-next/src/types/open-next.ts index 53023d5ae..1b8646349 100644 --- a/packages/open-next/src/types/open-next.ts +++ b/packages/open-next/src/types/open-next.ts @@ -80,6 +80,16 @@ export interface DangerousOptions { headersAndCookiesPriority?: ( event: InternalEvent, ) => "middleware" | "handler"; + + /** + * Configuration option to prioritize headers set via middleware over headers set via the option in the Next config. + * This brings OpenNext behavior inline with the documented execution order. + * + * See also {@link https://nextjs.org/docs/app/api-reference/file-conventions/middleware#execution-order} + * + * @default false + */ + middlewareHeadersOverrideNextConfigHeaders?: boolean; } export type BaseOverride = { diff --git a/packages/tests-e2e/tests/appRouter/headers.test.ts b/packages/tests-e2e/tests/appRouter/headers.test.ts index 348811a49..cd6b89f7b 100644 --- a/packages/tests-e2e/tests/appRouter/headers.test.ts +++ b/packages/tests-e2e/tests/appRouter/headers.test.ts @@ -28,3 +28,20 @@ test("Headers", async ({ page }) => { // Request ID header should be set expect(headers["x-opennext-requestid"]).not.toBeFalsy(); }); + +/** + * Tests that the middleware headers are applied after next.config.js headers. + */ +test("Headers execution order", async ({ page }) => { + const responsePromise = page.waitForResponse((response) => { + return response.status() === 200; + }); + await page.goto("/headers/execution-order"); + + const response = await responsePromise; + // Response header should be set + const headers = response.headers(); + + // The next.config.js headers is overwritten by the middleware + expect(headers["e2e-headers"]).toEqual("middleware"); +}); From e37e9affff2a01ceaf37d237af382e34ba7f69ea Mon Sep 17 00:00:00 2001 From: Dirk de Visser Date: Fri, 12 Sep 2025 15:45:23 +0200 Subject: [PATCH 2/5] chore: add initial changeset file --- .changeset/shiny-plants-remember.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/shiny-plants-remember.md diff --git a/.changeset/shiny-plants-remember.md b/.changeset/shiny-plants-remember.md new file mode 100644 index 000000000..b842e9bcc --- /dev/null +++ b/.changeset/shiny-plants-remember.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": minor +--- + +feat: Add option to align with Next.js execution order for headers in middleware and next.config.js From 9a2aed374666384c2c5e7076ccc45c5d3d8f5371 Mon Sep 17 00:00:00 2001 From: Dirk de Visser Date: Fri, 12 Sep 2025 20:20:20 +0200 Subject: [PATCH 3/5] chore: pick open-next.config.local.ts changes as well --- examples/app-router/open-next.config.local.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/app-router/open-next.config.local.ts b/examples/app-router/open-next.config.local.ts index 63d80481e..2f12ed08c 100644 --- a/examples/app-router/open-next.config.local.ts +++ b/examples/app-router/open-next.config.local.ts @@ -11,6 +11,11 @@ export default { }, }, + + dangerous: { + middlewareHeadersOverrideNextConfigHeaders: true, + }, + imageOptimization: { override: { wrapper: "dummy", From db41c3e3dc48fbf0a52961810734d616b628b9ba Mon Sep 17 00:00:00 2001 From: Dirk de Visser Date: Fri, 19 Sep 2025 16:05:54 +0200 Subject: [PATCH 4/5] chore: fix feedback --- examples/app-router/app/headers/execution-order/page.tsx | 9 --------- examples/app-router/middleware.ts | 3 ++- examples/app-router/open-next.config.local.ts | 1 - packages/open-next/src/types/open-next.ts | 3 ++- packages/tests-e2e/tests/appRouter/headers.test.ts | 2 +- 5 files changed, 5 insertions(+), 13 deletions(-) delete mode 100644 examples/app-router/app/headers/execution-order/page.tsx diff --git a/examples/app-router/app/headers/execution-order/page.tsx b/examples/app-router/app/headers/execution-order/page.tsx deleted file mode 100644 index cf041579c..000000000 --- a/examples/app-router/app/headers/execution-order/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export default async function HeadersExecutionOrder() { - return ( -
-

- Used to check execution order of middleware vs next.config headers. -

-
- ); -} diff --git a/examples/app-router/middleware.ts b/examples/app-router/middleware.ts index 3e472d45b..2c3a5fe34 100644 --- a/examples/app-router/middleware.ts +++ b/examples/app-router/middleware.ts @@ -51,8 +51,9 @@ export function middleware(request: NextRequest) { responseHeaders.set("response-header", "response-header"); // For dangerous.middlewareHeadersOverrideNextConfigHeaders we need to verify that middleware headers override next.config.js headers. - if (path === "/headers/execution-order") { + if (path === "/headers/override-from-middleware") { responseHeaders.set("e2e-headers", "middleware"); + return NextResponse.json({}, { headers: responseHeaders }); } // Set the cache control header with custom swr diff --git a/examples/app-router/open-next.config.local.ts b/examples/app-router/open-next.config.local.ts index 2f12ed08c..27e47e95d 100644 --- a/examples/app-router/open-next.config.local.ts +++ b/examples/app-router/open-next.config.local.ts @@ -11,7 +11,6 @@ export default { }, }, - dangerous: { middlewareHeadersOverrideNextConfigHeaders: true, }, diff --git a/packages/open-next/src/types/open-next.ts b/packages/open-next/src/types/open-next.ts index 1b8646349..b68ff3e39 100644 --- a/packages/open-next/src/types/open-next.ts +++ b/packages/open-next/src/types/open-next.ts @@ -83,7 +83,8 @@ export interface DangerousOptions { /** * Configuration option to prioritize headers set via middleware over headers set via the option in the Next config. - * This brings OpenNext behavior inline with the documented execution order. + * + * The default will change to 'true' in v4. * * See also {@link https://nextjs.org/docs/app/api-reference/file-conventions/middleware#execution-order} * diff --git a/packages/tests-e2e/tests/appRouter/headers.test.ts b/packages/tests-e2e/tests/appRouter/headers.test.ts index cd6b89f7b..ac9123032 100644 --- a/packages/tests-e2e/tests/appRouter/headers.test.ts +++ b/packages/tests-e2e/tests/appRouter/headers.test.ts @@ -36,7 +36,7 @@ test("Headers execution order", async ({ page }) => { const responsePromise = page.waitForResponse((response) => { return response.status() === 200; }); - await page.goto("/headers/execution-order"); + await page.goto("/headers/override-from-middleware"); const response = await responsePromise; // Response header should be set From 92ad1023241f336ec88e641ca3be9a2f4d7f8b3b Mon Sep 17 00:00:00 2001 From: Dirk de Visser Date: Fri, 19 Sep 2025 16:05:54 +0200 Subject: [PATCH 5/5] chore: improve comment on test --- packages/tests-e2e/tests/appRouter/headers.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tests-e2e/tests/appRouter/headers.test.ts b/packages/tests-e2e/tests/appRouter/headers.test.ts index ac9123032..69774b77b 100644 --- a/packages/tests-e2e/tests/appRouter/headers.test.ts +++ b/packages/tests-e2e/tests/appRouter/headers.test.ts @@ -30,9 +30,9 @@ test("Headers", async ({ page }) => { }); /** - * Tests that the middleware headers are applied after next.config.js headers. + * Tests that the middleware headers are applied after next.config.js headers. Requires 'dangerous.middlewareHeadersOverrideNextConfigHeaders' to be set. */ -test("Headers execution order", async ({ page }) => { +test("Middleware headers override next.config.js headers", async ({ page }) => { const responsePromise = page.waitForResponse((response) => { return response.status() === 200; });