Fix noindex is missing on static not-found page (#67135)

### What

Render noindex into a flight data and rsc payload when page path is
`/404`

### Why

When it's static generation, noindex is not rendered due to the
statusCode from mock request is 200, but we can relying on the pagePath
as `/404` page should always contain `nonidex`

We were missing the noindex before for flight generation, now we'll
render it when it's 404 page.
This commit is contained in:
Jiachi Liu 2024-06-23 21:56:08 +02:00 committed by GitHub
parent 16cf88e569
commit fc7f62dc4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 56 additions and 16 deletions

View file

@ -298,6 +298,17 @@ function makeGetDynamicParamFromSegment(
} }
} }
function NonIndex({ ctx }: { ctx: AppRenderContext }) {
const is404Page = ctx.pagePath === '/404'
const isInvalidStatusCode =
typeof ctx.res.statusCode === 'number' && ctx.res.statusCode > 400
if (is404Page || isInvalidStatusCode) {
return <meta name="robots" content="noindex" />
}
return null
}
// Handle Flight render request. This is only used when client-side navigating. E.g. when you `router.push('/dashboard')` or `router.reload()`. // Handle Flight render request. This is only used when client-side navigating. E.g. when you `router.push('/dashboard')` or `router.reload()`.
async function generateFlight( async function generateFlight(
ctx: AppRenderContext, ctx: AppRenderContext,
@ -344,8 +355,11 @@ async function generateFlight(
isFirst: true, isFirst: true,
// For flight, render metadata inside leaf page // For flight, render metadata inside leaf page
rscPayloadHead: ( rscPayloadHead: (
// Adding requestId as react key to make metadata remount for each render <>
<NonIndex ctx={ctx} />
{/* Adding requestId as react key to make metadata remount for each render */}
<MetadataTree key={requestId} /> <MetadataTree key={requestId} />
</>
), ),
injectedCSS: new Set(), injectedCSS: new Set(),
injectedJS: new Set(), injectedJS: new Set(),
@ -493,10 +507,7 @@ async function ReactServerApp({ tree, ctx, asNotFound }: ReactServerAppProps) {
couldBeIntercepted={couldBeIntercepted} couldBeIntercepted={couldBeIntercepted}
initialHead={ initialHead={
<> <>
{typeof ctx.res.statusCode === 'number' && <NonIndex ctx={ctx} />
ctx.res.statusCode > 400 && (
<meta name="robots" content="noindex" />
)}
{/* Adding requestId as react key to make metadata remount for each render */} {/* Adding requestId as react key to make metadata remount for each render */}
<MetadataTree key={ctx.requestId} /> <MetadataTree key={ctx.requestId} />
</> </>
@ -532,7 +543,6 @@ async function ReactServerError({
}, },
requestStore: { url }, requestStore: { url },
requestId, requestId,
res,
} = ctx } = ctx
const [MetadataTree] = createMetadataComponents({ const [MetadataTree] = createMetadataComponents({
@ -547,11 +557,9 @@ async function ReactServerError({
const head = ( const head = (
<> <>
<NonIndex ctx={ctx} />
{/* Adding requestId as react key to make metadata remount for each render */} {/* Adding requestId as react key to make metadata remount for each render */}
<MetadataTree key={requestId} /> <MetadataTree key={requestId} />
{typeof res.statusCode === 'number' && res.statusCode >= 400 && (
<meta name="robots" content="noindex" />
)}
{process.env.NODE_ENV === 'development' && ( {process.env.NODE_ENV === 'development' && (
<meta name="next-error" content="not-found" /> <meta name="next-error" content="not-found" />
)} )}
@ -1269,7 +1277,7 @@ async function renderToHTMLOrFlightImpl(
setHeader('Location', redirectUrl) setHeader('Location', redirectUrl)
} }
const is404 = res.statusCode === 404 const is404 = ctx.res.statusCode === 404
if (!is404 && !hasRedirectError && !shouldBailoutToCSR) { if (!is404 && !hasRedirectError && !shouldBailoutToCSR) {
res.statusCode = 500 res.statusCode = 500
} }

View file

@ -13,11 +13,6 @@
"app dir - metadata react cache should have same title and page value when navigating" "app dir - metadata react cache should have same title and page value when navigating"
] ]
}, },
"test/e2e/app-dir/metadata-navigation/metadata-navigation.test.ts": {
"failed": [
"app dir - metadata navigation navigation should render root not-found with default metadata"
]
},
"test/e2e/middleware-rewrites/test/index.test.ts": { "test/e2e/middleware-rewrites/test/index.test.ts": {
"failed": ["Middleware Rewrite should handle catch-all rewrite correctly"] "failed": ["Middleware Rewrite should handle catch-all rewrite correctly"]
} }

View file

@ -0,0 +1,3 @@
export default function Page() {
return <h1>Foo</h1>
}

View file

@ -0,0 +1,7 @@
export default function Layout({ children }) {
return (
<html>
<body>{children}</body>
</html>
)
}

View file

@ -0,0 +1,27 @@
import { nextTestSetup } from 'e2e-utils'
const isPPREnabled = process.env.__NEXT_EXPERIMENTAL_PPR === 'true'
describe('app dir - not-found - default', () => {
const { next, isNextStart } = nextTestSetup({
files: __dirname,
skipDeployment: true,
})
it('should has noindex in the head html', async () => {
const $ = await next.render$('/does-not-exist')
expect(await $('meta[name="robots"]').attr('content')).toBe('noindex')
})
if (isNextStart) {
it('should contain noindex contain in the page', async () => {
const html = await next.readFile('.next/server/app/_not-found.html')
const rsc = await next.readFile(
`.next/server/app/_not-found.${isPPREnabled ? 'prefetch.' : ''}rsc`
)
expect(html).toContain('noindex')
expect(rsc).toContain('noindex')
})
}
})