Skip to content

Commit 6797a08

Browse files
authored
Fix: Do not force BLOCKING_STATIC_RENDER for DOM bots (#82427)
The issue was that DOM bots with PPR-enabled routes were being forced to use BLOCKING_STATIC_RENDER mode, which prevented them from using the fallback cache lookup mechanism. This caused them to generate new content with isRevalidating: true, which blocks postponed data and triggers the Math.random() static generation bailout error. For HTML bots, they keep using BLOCKING_STATIC_RENDER because SSG was turned off for them and won't see the SSG bailout error. For DOM bots, they should behave like a normal user. Fixes NAR-273
1 parent dcece12 commit 6797a08

File tree

5 files changed

+96
-3
lines changed

5 files changed

+96
-3
lines changed

packages/next/src/build/templates/app-page.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -653,11 +653,13 @@ export async function handler(
653653
fallbackMode = parseFallbackField(prerenderInfo.fallback)
654654
}
655655

656-
// When serving a bot request, we want to serve a blocking render and not
657-
// the prerendered page. This ensures that the correct content is served
656+
// When serving a HTML bot request, we want to serve a blocking render and
657+
// not the prerendered page. This ensures that the correct content is served
658658
// to the bot in the head.
659659
if (fallbackMode === FallbackMode.PRERENDER && isBot(userAgent)) {
660-
fallbackMode = FallbackMode.BLOCKING_STATIC_RENDER
660+
if (!isRoutePPREnabled || isHtmlBot) {
661+
fallbackMode = FallbackMode.BLOCKING_STATIC_RENDER
662+
}
661663
}
662664

663665
if (previousCacheEntry?.isStale === -1) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react'
2+
3+
import type { Metadata } from 'next'
4+
5+
export async function generateMetadata(): Promise<Metadata> {
6+
await new Promise((resolve) => setTimeout(resolve, 1000)) // Simulate a delay
7+
return {
8+
title: 'Home',
9+
description: 'Welcome to the home page',
10+
}
11+
}
12+
13+
export default async function Home({
14+
params,
15+
}: {
16+
params: Promise<{ slug: string }>
17+
}) {
18+
const { slug } = await params
19+
// Math.random() will cause SSG_BAILOUT if static generation runs
20+
// Bots should bypass static generation in PPR to avoid this error
21+
const randomValue = Math.random()
22+
23+
return (
24+
<>
25+
<h1>{slug}</h1>
26+
<p>{randomValue}</p>
27+
</>
28+
)
29+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React, { Suspense } from 'react'
2+
import type { Metadata } from 'next'
3+
4+
export const metadata: Metadata = {
5+
title: 'PPR Bot SSG Bypass Test',
6+
description:
7+
'Test bot static generation bypass with PPR and cache components',
8+
}
9+
10+
export default function RootLayout({
11+
children,
12+
}: {
13+
children: React.ReactNode
14+
}) {
15+
return (
16+
<html lang="en">
17+
<body>
18+
<Suspense>{children}</Suspense>
19+
</body>
20+
</html>
21+
)
22+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
3+
describe('cache-components PPR bot static generation bypass', () => {
4+
const { next } = nextTestSetup({
5+
files: __dirname,
6+
})
7+
8+
it('should bypass static generation for DOM bot requests to avoid SSG_BAILOUT', async () => {
9+
const res = await next.fetch('/foo', {
10+
headers: {
11+
'user-agent': 'Googlebot',
12+
},
13+
})
14+
// With cache components + PPR enabled, DOM bots should behave like regular users
15+
// and use the fallback cache mechanism. This allows them to handle dynamic content
16+
// like Math.random() without triggering SSG_BAILOUT errors.
17+
expect(res.status).toBe(200)
18+
19+
// Verify that the response contains the page content
20+
const html = await res.text()
21+
22+
// Check that the page rendered successfully
23+
// With PPR, content is streamed via script tags
24+
expect(html).toContain('\\"children\\":\\"foo\\"')
25+
26+
// Verify Math.random() was executed (check for a decimal number in the streamed content)
27+
expect(html).toMatch(/\\"children\\":0\.\d+/)
28+
29+
// With PPR, content is streamed, but the important thing is that
30+
// the page rendered without a 500 error
31+
})
32+
})
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
experimental: {
4+
cacheComponents: true,
5+
},
6+
}
7+
8+
module.exports = nextConfig

0 commit comments

Comments
 (0)