diff --git a/examples/app-router/app/auth-interrupts/forbidden/page.tsx b/examples/app-router/app/auth-interrupts/forbidden/page.tsx
new file mode 100644
index 000000000..9e9a10b78
--- /dev/null
+++ b/examples/app-router/app/auth-interrupts/forbidden/page.tsx
@@ -0,0 +1,8 @@
+import { forbidden } from "next/navigation";
+
+export default function Page() {
+ forbidden();
+
+ // this should never be rendered
+ return <>>;
+}
diff --git a/examples/app-router/app/auth-interrupts/unauthorized/page.tsx b/examples/app-router/app/auth-interrupts/unauthorized/page.tsx
new file mode 100644
index 000000000..d611f5250
--- /dev/null
+++ b/examples/app-router/app/auth-interrupts/unauthorized/page.tsx
@@ -0,0 +1,8 @@
+import { unauthorized } from "next/navigation";
+
+export default function Page() {
+ unauthorized();
+
+ // this should never be rendered
+ return <>>;
+}
diff --git a/examples/app-router/app/forbidden.tsx b/examples/app-router/app/forbidden.tsx
new file mode 100644
index 000000000..3c2521184
--- /dev/null
+++ b/examples/app-router/app/forbidden.tsx
@@ -0,0 +1,11 @@
+import Link from "next/link";
+
+export default function Forbidden() {
+ return (
+
+
Forbidden
+
You are not authorized to access this resource.
+ Return Home
+
+ );
+}
diff --git a/examples/app-router/app/unauthorized.tsx b/examples/app-router/app/unauthorized.tsx
new file mode 100644
index 000000000..7a8bbc3dd
--- /dev/null
+++ b/examples/app-router/app/unauthorized.tsx
@@ -0,0 +1,7 @@
+export default function Unauthorized() {
+ return (
+
+
Unauthorized
+
+ );
+}
diff --git a/examples/app-router/next.config.ts b/examples/app-router/next.config.ts
index 8a85d80a7..8ad1b21df 100644
--- a/examples/app-router/next.config.ts
+++ b/examples/app-router/next.config.ts
@@ -17,6 +17,9 @@ const nextConfig: NextConfig = {
},
],
},
+ experimental: {
+ authInterrupts: true,
+ },
redirects: async () => {
return [
{
diff --git a/packages/open-next/src/http/openNextResponse.ts b/packages/open-next/src/http/openNextResponse.ts
index 5db512ebf..14f81d91a 100644
--- a/packages/open-next/src/http/openNextResponse.ts
+++ b/packages/open-next/src/http/openNextResponse.ts
@@ -14,6 +14,7 @@ import { parseCookies, parseHeaders } from "./util";
const SET_COOKIE_HEADER = "set-cookie";
const CANNOT_BE_USED = "This cannot be used in OpenNext";
+const ERROR_CODES = [401, 403, 404, 500];
// We only need to implement the methods that are used by next.js
export class OpenNextNodeResponse extends Transform implements ServerResponse {
@@ -375,15 +376,15 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse {
return this;
}
- // For some reason, next returns the 500 error page with some cache-control headers
+ // For some reason, next returns the error pages with some cache-control headers
// We need to fix that
private fixHeadersForError() {
if (process.env.OPEN_NEXT_DANGEROUSLY_SET_ERROR_HEADERS === "true") {
return;
}
- // We only check for 404 and 500 errors
+ // We only check for 401, 403, 404 and 500 errors
// The rest should be errors that are handled by the user and they should set the cache headers themselves
- if (this.statusCode === 404 || this.statusCode === 500) {
+ if (ERROR_CODES.includes(this.statusCode)) {
// For some reason calling this.setHeader("Cache-Control", "no-cache, no-store, must-revalidate") does not work here
// The function is not even called, i'm probably missing something obvious
this.headers["cache-control"] =
diff --git a/packages/tests-e2e/tests/appRouter/auth-interrupts.test.ts b/packages/tests-e2e/tests/appRouter/auth-interrupts.test.ts
new file mode 100644
index 000000000..004f0b5d0
--- /dev/null
+++ b/packages/tests-e2e/tests/appRouter/auth-interrupts.test.ts
@@ -0,0 +1,28 @@
+import { expect, test } from "@playwright/test";
+
+const NO_CACHE_HEADER =
+ "private, no-cache, no-store, max-age=0, must-revalidate";
+
+test("test forbidden", async ({ page }) => {
+ const result = await page.goto("/auth-interrupts/forbidden");
+ expect(result).toBeDefined();
+ expect(result?.status()).toBe(403);
+
+ const headers = result?.headers();
+ expect(headers?.["cache-control"]).toBe(NO_CACHE_HEADER);
+
+ const heading = page.getByText("Forbidden");
+ await expect(heading).toBeVisible();
+});
+
+test("test unauthorized", async ({ page }) => {
+ const result = await page.goto("/auth-interrupts/unauthorized");
+ expect(result).toBeDefined();
+ expect(result?.status()).toBe(401);
+
+ const headers = result?.headers();
+ expect(headers?.["cache-control"]).toBe(NO_CACHE_HEADER);
+
+ const heading = page.getByText("Unauthorized");
+ await expect(heading).toBeVisible();
+});