Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shiny-plants-remember.md
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions examples/app-router/app/headers/execution-order/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default async function HeadersExecutionOrder() {
return (
<div>
<h1>
Used to check execution order of middleware vs next.config headers.
</h1>
</div>
);
}
5 changes: 5 additions & 0 deletions examples/app-router/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")) {
Expand Down
3 changes: 3 additions & 0 deletions examples/app-router/open-next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const config = {
},
},
functions: {},
dangerous: {
middlewareHeadersOverrideNextConfigHeaders: true,
},
buildCommand: "npx turbo build",
};

Expand Down
20 changes: 16 additions & 4 deletions packages/open-next/src/core/routingHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
10 changes: 10 additions & 0 deletions packages/open-next/src/types/open-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
17 changes: 17 additions & 0 deletions packages/tests-e2e/tests/appRouter/headers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});