`next/link` prefetches across routers in mixed App/Pages apps (`_next/data` 404 for App routes, HTML prefetch for Pages routes)
Link to the code that reproduces this issue
https://github.com/KnotzerIO/next-cross-router-prefetch-repro
Deployed: https://next-cross-router-prefetch-repro.vercel.app
To Reproduce
npm i && npm run build && npm start(production build, prefetching is disabled innext dev)- Open http://localhost:3456/about with the Network tab open, filter for
_next/data - Scroll the
/dashboardlink into view, then hover it - Open http://localhost:3456/dashboard, filter for
_rsc - Optional:
npx playwright install chromium && node scripts/record.mjsprints every request as a table
The app has one App Router page (app/dashboard) and two Pages Router routes: pages/about.js (SSG) and an SSG optional catch-all pages/[[...slug]].js. No next.config.
Current vs. Expected behavior
A) Pages Router link to an App Router route
On /about, <Link href="/dashboard"> requests /_next/data/<buildId>/dashboard.json?slug=dashboard on viewport prefetch and again on hover. It returns 404. The click itself works, the client router filter hard navigates to the App route.

Expected: no data request for a path the client router filter already identified as an App Router route.
In prefetch() the filter match only stores a marker and the function keeps going:
if (await this._bfl(asPath, resolvedAs, options.locale, true)) {
// stores { __appRouter: true }
}
await Promise.all([
this.pageLoader._isSsg(route).then((isSsg) => isSsg ? fetchNextData({ ... }) : false),
...
])route resolves to the SSG catch-all, so fetchNextData runs. Any SSG Pages route with a dynamic segment that matches the path triggers it (pages/[slug].js too, fallback: false makes no difference). prefetch={false} does not help: on the Pages Router it only disables the viewport prefetch and hover still calls prefetch(), as documented. With the early return that hover prefetch would also stop hitting _next/data:
This looks like a missing early return after the filter match. change() hard navigates on every filter match anyway, so nothing fetched after the match is ever used. The fixes for #98180 and #98181 (#98182, #98187) touched the same block, but not this path.
B) App Router link to a Pages Router route
On /dashboard, <Link href="/about"> sends a viewport prefetch GET /about?_rsc=... with RSC: 1 and Next-Router-Prefetch: 1. The Pages route answers with the full HTML document, which is thrown away because it is not a Flight response.


Expected, per the migration guide:
When navigating between routes served by the different Next.js routers, there will be a hard navigation. Automatic link prefetching with
next/linkwill not prefetch across routers.
So either no automatic prefetch for a Pages route, or the docs should say that it happens.
On Vercel the same requests are sent, but they come back as a 2 byte application/octet-stream body instead of the HTML document. With next start every prefetch downloads the whole page.
Related: on click the App Router sends one more RSC request, gets HTML again and falls back to a document navigation. This also happens with prefetch={false} (/dashboard/no-prefetch in the repo). The docs do not cover this case, but it adds a round trip to every App to Pages navigation:
Recorded requests (16.3.6, next start)
| Start page | Step | Request | Headers | Status | Content-Type |
|---|---|---|---|---|---|
/about |
load | /_next/data/<id>/dashboard.json?slug=dashboard |
prefetch | 404 | |
/about |
hover | /_next/data/<id>/dashboard.json?slug=dashboard |
prefetch | 404 | |
/about |
click | document /dashboard |
200 | text/html | |
/dashboard |
load | /about?_rsc=j79V... |
RSC: 1, prefetch |
200 | text/html |
/dashboard |
click | /about?_rsc=T1N9... |
RSC: 1 |
200 | text/html |
/dashboard |
click | document /about |
200 | text/html | |
/dashboard/no-prefetch |
click | /about?_rsc=zTtM... |
RSC: 1 |
200 | text/html |
/dashboard/no-prefetch |
click | document /about |
200 | text/html |
Same result on 16.4.0-canary.51. A is identical on the Vercel deployment.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 25.6.0
Available memory (MB): 49152
Available CPU cores: 12
Binaries:
Node: 24.20.0
npm: 11.19.0
Relevant Packages:
next: 16.3.6
react: 19.2.0
react-dom: 19.2.0
Next.js Config:
output: N/AWhich area(s) are affected? (Select all that apply)
Linking and Navigating, Pages Router
Which stage(s) are affected? (Select all that apply)
next build (local), next start (local), Vercel (Deployed), Other (Deployed)
Additional context
We run a large Pages Router app and are migrating section by section to the App Router, as the migration guide suggests. In production this shows up as a 404 in the network log for every Pages page that links into the App section, and as full HTML documents downloaded for every App page that links back.
Source: vercel/next.js