diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 40e2397e53935..53ab14ff2ba31 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -76,8 +76,8 @@ import { MIDDLEWARE_REACT_LOADABLE_MANIFEST, SERVER_REFERENCE_MANIFEST, FUNCTIONS_CONFIG_MANIFEST, - UNDERSCORE_NOT_FOUND_ROUTE_ENTRY, UNDERSCORE_NOT_FOUND_ROUTE, + UNDERSCORE_NOT_FOUND_ROUTE_ENTRY, DYNAMIC_CSS_MANIFEST, TURBOPACK_CLIENT_MIDDLEWARE_MANIFEST, } from '../shared/lib/constants' @@ -3090,12 +3090,6 @@ export default async function build( ] for (const prerenderedRoute of prerenderedRoutes) { - // TODO: check if still needed? - // Exclude the /_not-found route. - if (prerenderedRoute.pathname === UNDERSCORE_NOT_FOUND_ROUTE) { - continue - } - if ( isRoutePPREnabled && prerenderedRoute.fallbackRouteParams && @@ -3114,7 +3108,6 @@ export default async function build( // Handle all the static routes. for (const route of staticPrerenderedRoutes) { if (isDynamicRoute(page) && route.pathname === page) continue - if (route.pathname === UNDERSCORE_NOT_FOUND_ROUTE) continue const { metadata = {}, @@ -3164,9 +3157,13 @@ export default async function build( } const meta = collectMeta(metadata) + const status = + route.pathname === UNDERSCORE_NOT_FOUND_ROUTE + ? 404 + : meta.status prerenderManifest.routes[route.pathname] = { - initialStatus: meta.status, + initialStatus: status, initialHeaders: meta.headers, renderingMode: isAppPPREnabled ? isRoutePPREnabled diff --git a/packages/next/src/export/index.ts b/packages/next/src/export/index.ts index 9047b40a4375b..d3788dc3595df 100644 --- a/packages/next/src/export/index.ts +++ b/packages/next/src/export/index.ts @@ -763,6 +763,10 @@ async function exportAppImpl( if (!options.buildExport && prerenderManifest) { await Promise.all( Object.keys(prerenderManifest.routes).map(async (unnormalizedRoute) => { + // Skip handling /_not-found route, it will copy the 404.html file later + if (unnormalizedRoute === '/_not-found') { + return + } const { srcRoute } = prerenderManifest!.routes[unnormalizedRoute] const appPageName = mapAppRouteToPage.get(srcRoute || '') const pageName = appPageName || srcRoute || unnormalizedRoute diff --git a/test/e2e/app-dir/app-static/app-static.test.ts b/test/e2e/app-dir/app-static/app-static.test.ts index 09034c8b51cbd..0548ec591adca 100644 --- a/test/e2e/app-dir/app-static/app-static.test.ts +++ b/test/e2e/app-dir/app-static/app-static.test.ts @@ -987,6 +987,31 @@ describe('app-dir static/dynamic handling', () => { "initialRevalidateSeconds": false, "srcRoute": "/", }, + "/_not-found": { + "allowHeader": [ + "host", + "x-matched-path", + "x-prerender-revalidate", + "x-prerender-revalidate-if-generated", + "x-next-revalidated-tags", + "x-next-revalidate-tag-token", + ], + "dataRoute": "/_not-found.rsc", + "experimentalBypassFor": [ + { + "key": "next-action", + "type": "header", + }, + { + "key": "content-type", + "type": "header", + "value": "multipart/form-data;.*", + }, + ], + "initialRevalidateSeconds": false, + "initialStatus": 404, + "srcRoute": "/_not-found", + }, "/api/large-data": { "allowHeader": [ "host", diff --git a/test/e2e/app-dir/cache-components-dynamic-imports/cache-components-dynamic-imports.test.ts b/test/e2e/app-dir/cache-components-dynamic-imports/cache-components-dynamic-imports.test.ts index 9dae3d2cb18a8..7b96136984f61 100644 --- a/test/e2e/app-dir/cache-components-dynamic-imports/cache-components-dynamic-imports.test.ts +++ b/test/e2e/app-dir/cache-components-dynamic-imports/cache-components-dynamic-imports.test.ts @@ -39,6 +39,7 @@ describe('async imports in cacheComponents', () => { expect(prerenderedRoutes).toMatchInlineSnapshot(` [ + "/_not-found", "/inside-render/client/async-module", "/inside-render/client/sync-module", "/inside-render/route-handler/async-module", diff --git a/test/e2e/app-dir/metadata-static-generation/metadata-static-generation.test.ts b/test/e2e/app-dir/metadata-static-generation/metadata-static-generation.test.ts index e6765a7c12d77..03960b5789a84 100644 --- a/test/e2e/app-dir/metadata-static-generation/metadata-static-generation.test.ts +++ b/test/e2e/app-dir/metadata-static-generation/metadata-static-generation.test.ts @@ -24,6 +24,7 @@ const isPPREnabled = process.env.__NEXT_EXPERIMENTAL_PPR === 'true' const staticRoutes = prerenderManifest.routes expect(Object.keys(staticRoutes).sort()).toEqual([ '/', + '/_not-found', '/suspenseful/static', ]) }) diff --git a/test/e2e/app-dir/metadata-streaming-static-generation/metadata-streaming-static-generation.test.ts b/test/e2e/app-dir/metadata-streaming-static-generation/metadata-streaming-static-generation.test.ts index 343e1e3b21d85..7800c4548774d 100644 --- a/test/e2e/app-dir/metadata-streaming-static-generation/metadata-streaming-static-generation.test.ts +++ b/test/e2e/app-dir/metadata-streaming-static-generation/metadata-streaming-static-generation.test.ts @@ -22,6 +22,7 @@ const isPPREnabled = process.env.__NEXT_EXPERIMENTAL_PPR === 'true' const staticRoutes = prerenderManifest.routes expect(Object.keys(staticRoutes).sort()).toEqual([ '/', + '/_not-found', '/slow/static', '/suspenseful/static', ]) diff --git a/test/e2e/app-dir/use-cache/use-cache.test.ts b/test/e2e/app-dir/use-cache/use-cache.test.ts index eda32ed240b53..3f2ea8c44d2b8 100644 --- a/test/e2e/app-dir/use-cache/use-cache.test.ts +++ b/test/e2e/app-dir/use-cache/use-cache.test.ts @@ -473,6 +473,7 @@ describe('use-cache', () => { expect(prerenderedRouteKeys).toEqual( [ + '/_not-found', // [id] route, first entry in generateStaticParams expect.stringMatching(/\/a\d/), withCacheComponents && '/api',