Files
Tim NeutkensandGitHub f2ddd134e7 Fix non-RSC router prefetch handling (#94553)
### What?

Ignore router prefetch protocol headers when the request is not
classified as an RSC request. Add a dedicated force-dynamic App Router
fixture that verifies HTML and valid RSC prefetch behavior.

### Why?

A non-RSC HTML request with `Next-Router-Prefetch: 1` could enter the
partial prefetch tree path, produce a null active cache node, and
suspend the HTML render until timeout.

### How?

Determine `isRSCRequest` before parsing prefetch modes, then require it
for prefetch modes `1`, `2`, and `3`, including route-tree prefetch
classification. The regression fixture uses an abort timeout to fail
quickly if the HTML response hangs and separately confirms RSC
prefetches remain valid.

### Verification

- `pnpm build-all`
- `NEXT_TEST_PREFER_OFFLINE=1 pnpm test-start-turbo
test/e2e/app-dir/non-rsc-router-prefetch/non-rsc-router-prefetch.test.ts`
- `NEXT_TEST_PREFER_OFFLINE=1 pnpm test-start-webpack
test/e2e/app-dir/non-rsc-router-prefetch/non-rsc-router-prefetch.test.ts`
- `pnpm --filter=next types`

<!-- NEXT_JS_LLM_PR -->
2026-06-17 17:41:01 +02:00

48 lines
1.1 KiB
TypeScript

import { nextTestSetup } from 'e2e-utils'
import {
NEXT_ROUTER_PREFETCH_HEADER,
RSC_HEADER,
} from 'next/dist/client/components/app-router-headers'
describe('non-rsc-router-prefetch', () => {
const { next, skipped } = nextTestSetup({
files: __dirname,
skipDeployment: true,
})
if (skipped) {
return
}
beforeAll(async () => {
const res = await next.fetch('/')
await res.text()
})
it('ignores the router prefetch header for HTML requests', async () => {
const res = await next.fetch('/', {
headers: {
[NEXT_ROUTER_PREFETCH_HEADER]: '1',
},
signal: AbortSignal.timeout(5_000),
})
const html = await res.text()
expect(res.status).toBe(200)
expect(res.headers.get('content-type')).toContain('text/html')
expect(html).toContain('hello world')
})
it('honors the router prefetch header for RSC requests', async () => {
const res = await next.fetch('/', {
headers: {
[RSC_HEADER]: '1',
[NEXT_ROUTER_PREFETCH_HEADER]: '1',
},
})
expect(res.status).toBe(200)
expect(res.headers.get('content-type')).toContain('text/x-component')
})
})