Next 16.0.0/16.0.1-canary.2: next/link Prefetch Causes 404 for RSC Payloads in Static Export
Link to the code that reproduces this issue
https://github.com/Aaakul/next16-next-link-rsc-issue
To Reproduce
- Clone the reproduction repository
- Run
cd next16 - Run
npm install - Run
npm run build(output: 'export' is already set in next.config.ts) - Run
npx serve out - Visit
/next16in a browser (e.g., http://localhost:3000/next16), open developer tools and check the console for 404 errors. Click the link to navigate to/next16/page, refresh and check if similar errors appears. - Run
cd .. - Run
cd next15 - Repeat 3~6
Current vs. Expected behavior
Current Behavior (Next.js 16.0.0/16.0.1-canary.2/16.0.1 + output: 'export'):
When visiting static exported page has next/link with default prefetching or is explicitly set to true / "auto" / null, the page attempts to fetch the target page's RSC Payload from incorrect path, resulting in a 404 Not Found error (e.g., '.../page/__next.page.PAGE.txt?_rsc=...').
Expected Behavior (Next.js 15.5.6 + output: 'export'):
Invalid RSC requests are not expected in statically exported pages
Provide environment information
Operating System:
Platform: win32
Arch: x64
Version: Windows 10 Home
Available memory (MB): 32578
Available CPU cores: 16
Binaries:
Node: 20.19.5
npm: 10.8.2
Yarn: N/A
pnpm: N/A
Relevant Packages:
next: 16.0.1
eslint-config-next: N/A
react: 19.2.0
react-dom: 19.2.0
typescript: 5.9.3
Next.js Config:
output: export
Which area(s) are affected? (Select all that apply)
Linking and Navigating, Output
Which stage(s) are affected? (Select all that apply)
next build (local), Other (Deployed)
Additional context
- Reproducible Versions: Next.js 16.0.0, 16.0.1-canary.2 and 16.0.1
- This issue does not exist in Next.js 15.5.6
- This issue is reproducible locally using npx serve out and is also observed on static hosting platforms like GitHub Pages: https://aaakul.github.io/next16/ and Cloudflare Pages: https://next16-next-link-rsc-issue.pages.dev/
- There are differences in stability of reproducible across browsers
Comparing my demo's output .txt files:
- Next 15.5.6:
index.txtpage.txt - Next 16.0.1-canary.2:
│ index.txt
│ page.txt
│ _not-found.txt
│ __next._full.txt
│ __next._index.txt
│ __next._tree.txt
│ __next.__PAGE__.txt
│
|─page
│ │ __next.page.txt
│ │ __next._full.txt
│ │ __next._index.txt
│ │ __next._tree.txt
│ │
│ └─__next.page
│ __PAGE__.txt
│
└─_not-found
│ __next._full.txt
│ __next._index.txt
│ __next._not-found.txt
│ __next._tree.txt
└─__next._not-found
__PAGE__.txt
Same happens for non-static exports as well, though only when deploying to Vercel for some reason: https://github.com/vercel/next.js/issues/85349
Hi @Aaakul --
I took a look at your reproduction, and both apps are 404ing when I run them locally with your instructions. I didn't see anything specific to Next 16 in here, but it looks like you might have stumbled across a bug with basePath when combined with output: export (see: https://github.com/vercel/next.js/issues/67817)
Your deployed demo also seems to be working as expected.
Can you clarify if this is just an issue with basePath or something novel?
@ztanner For this demo, make sure you're accessing the correct paths (/next16) after building it, then running serve. This is not related to basePath specifically, I have an app locally which does the same without basePath set. Also see the issue I linked above, does the same thing.
I dug deeper into it and found out that Next is resolving incorrect paths when querying for RSC data, here's an example from my local repo below:
(top is what Next requests, bottom is what Next should request)
/hello/sq-1213/__next.!KGV2bSk.hello.$d$slugger.__PAGE__.txt?_rsc=69ns0
/hello/sq-1213/__next.!KGV2bSk/hello/$d$slugger/__PAGE__.txt?_rsc=69ns0
This is for a page with statically generated dynamic params /hello/[slugger]
Same happens for the linked demo in this issue, easily reproducible by opening the linked demo deployment. (You'll see a 404 in the network panel for the path listed below)
(top is what Next requests, bottom is what Next should request)
/page/__next.page.__PAGE__.txt?_rsc=g7psd (link - 404s)
/page/__next.page/__PAGE__.txt?_rsc=g7psd (link)
Hi @Aaakul --
I took a look at your reproduction, and both apps are 404ing when I run them locally with your instructions. I didn't see anything specific to Next 16 in here, but it looks like you might have stumbled across a bug with
basePathwhen combined withoutput: export(see: #67817)Your deployed demo also seems to be working as expected.
Can you clarify if this is just an issue with
basePathor something novel?
Hi ztanner,
Thank you very much for taking the time to test!
I think the issue I reported is not related to basePath. I initially created a version without basePath. Adding basePath is for deploying the test demo on GitHub Pages. I have deployed a version using Next.js 16.0.1 on Cloudflare Pages that has no basePath configured: Cloudflare Pages URL with Next 16.0.1 and NO basePath
When visiting this link with Latest Chrome, Edge or Firefox, the 404 error is still consistently reproducible.
I also noticed some differences in stability across browsers on the previously provided GitHub Pages demo (Next 16.0.0 with basePath) and Cloudflare Pages demo (Next 16.0.1 without basePath):
- Latest Chrome: The 404 error is consistently reproduced both on GitHub Pages and Cloudflare Pages.
- Latest Firefox: The 404 error is consistently reproduced on Cloudflare Pages, not ocurr on GitHub Pages.
- Latest Edge: The 404 error is consistently reproduced on Cloudflare Pages. For GitHub Pages demo, the error is sometimes not ocurr, or it flashes quickly. By enabling "Preserve Log", I confirmed that the fleeting error is from the exact same function within the same JavaScript chunk that shows in Chrome/Firefox. This function appears to be responsible for handling the RSC payload request.
Do you need me to provide any additional testing or information?
@ztanner For this demo, make sure you're accessing the correct paths (/next16) after building it, then running
serve. This is not related tobasePathspecifically, I have an app locally which does the same withoutbasePathset. Also see the issue I linked above, does the same thing.I dug deeper into it and found out that Next is resolving incorrect paths when querying for RSC data, here's an example from my local repo below:
(top is what Next requests, bottom is what Next should request)
/hello/sq-1213/__next.!KGV2bSk.hello.$d$slugger.__PAGE__.txt?_rsc=69ns0/hello/sq-1213/__next.!KGV2bSk/hello/$d$slugger/__PAGE__.txt?_rsc=69ns0This is for a page with statically generated dynamic params
/hello/[slugger]Same happens for the linked demo in this issue, easily reproducible by opening the linked demo deployment. (You'll see a 404 in the network panel for the path listed below)
(top is what Next requests, bottom is what Next should request)
/page/__next.page.__PAGE__.txt?_rsc=g7psd(link - 404s)/page/__next.page/__PAGE__.txt?_rsc=g7psd(link)
Hi akshatmittal, Thanks so much for digging into this.
Based on my testing, the error not occur may be related to the browser platform. You can find the details in my reply to ztanner.
I also noticed some differences in stability across browsers
@Aaakul Very interesting that you are seeing differences based on browsers, I'm not seeing that for my local app. Tested in Chrome, Edge, Firefox and Comet. I'm able to reproduce this behaviour in every browser.
The exact same paths I mentioned in my previous reply from the demo also apply to the Cloudflare deployment that @Aaakul provided, so does seem like it's the path resolution issue for RSC payloads I've described.
@ztanner Also confirmed this exists in the latest canary.
Having the same issues for the non-static pages
@Aaakul, @akshatmittal did you find any workaround??
Having the same issues for the non-static pages
@Aaakul, @akshatmittal did you find any workaround??
Currently, I'm using next/link with prefetch={false}. In App Router, this will not prefetch any data, even on hover. Haven't tested in non-static export yet.
<Link
prefetch={false}
{...rest}
/>
Just wanted to chime to say that this doesn't appear to be confined to static exports, but occurs for both SSR'd and static pages. I'm not using a basePath nor an output in my next config (see below). Further digging points to the prefetch request header not being handled properly server-side...
Next Config
import type { NextConfig } from "next";
const inStaging = process.env.NEXT_PUBLIC_IN_DEVELOPMENT === "true";
const nextConfig: NextConfig = {
experimental: {
turbopackFileSystemCacheForDev: true
},
compiler: {
reactRemoveProperties:
process.env.NODE_ENV === "production" && !inStaging ? { properties: ["^data-testid$"] } : false
},
allowedDevOrigins: ["127.0.0.1", "localhost"],
devIndicators: false,
trailingSlash: true,
images: {
minimumCacheTTL: 2678400,
qualities: [60, 75, 100],
remotePatterns: [
{
protocol: "http",
hostname: "127.0.0.1",
port: "54321",
pathname: "/**"
},
// Production domains
{
protocol: "https",
hostname: "example.com",
pathname: "/**"
},
{
protocol: "https",
hostname: "example.dev",
pathname: "/**"
},
// Vercel preview URLs
{
protocol: "https",
hostname: "*.vercel.app",
pathname: "/**"
},
{
protocol: "https",
hostname: "i.imgur.com",
pathname: "/**"
},
{
protocol: "https",
hostname: "lh3.googleusercontent.com",
pathname: "/a/**"
},
{
protocol: "https",
hostname: "media.licdn.com",
pathname: "/dms/image/**"
}
]
},
async headers() {
const securityHeaders = [
{
key: "X-DNS-Prefetch-Control",
value: "on"
},
{
key: "X-Frame-Options",
value: "SAMEORIGIN"
},
{
key: "X-XSS-Protection",
value: "1; mode=block"
},
{
key: "Referrer-Policy",
value: "origin-when-cross-origin"
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload"
},
{
key: "X-Content-Type-Options",
value: "nosniff"
}
];
if (inStaging) {
securityHeaders.push({
key: "X-Robots-Tag",
value: "noindex, nofollow"
});
}
return [
{
source: "/(.*)",
headers: securityHeaders
}
];
}
};
export default nextConfig;
For this example, I have a /sign-in/ page (dynamic) that has a prefetch link to /sign-up/?_rsc=7dz3b (dynamic).
Request to remote URL hosted on Vercel without headers:
curl -IL --max-redirs 1 'https://www.example.com/sign-up/?_rsc=7dz3b'
HTTP/2 200
age: 0
cache-control: private, no-cache, no-store, max-age=0, must-revalidate
content-type: text/html; charset=utf-8
date: Wed, 12 Nov 2025 21:11:03 GMT
link: </_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2"
referrer-policy: origin-when-cross-origin
server: Vercel
strict-transport-security: max-age=63072000; includeSubDomains; preload
vary: rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch
x-content-type-options: nosniff
x-current-path: /sign-up/
x-dns-prefetch-control: on
x-frame-options: SAMEORIGIN
x-matched-path: /sign-up
x-powered-by: Next.js
x-vercel-cache: MISS
x-vercel-id: pdx1::sfo1::jfjkf-1762981862970-894886927fd1
x-xss-protection: 1; mode=block
Request to remote URL hosted on Vercel with headers:
curl -IL --max-redirs 1 'https://www.example.com/sign-up/?_rsc=7dz3b'\
-H 'accept: */*' \
-H 'accept-language: en-US,en;q=0.8' \
-H 'next-router-prefetch: 1' \
-H 'next-router-segment-prefetch: /_tree' \
-H 'next-url: /sign-in' \
-H 'priority: i' \
-H 'referer: https://www.example.com/sign-in/' \
-H 'rsc: 1' \
-H 'sec-ch-ua: "Chromium";v="142", "Brave";v="142", "Not_A Brand";v="99"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "macOS"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: same-origin' \
-H 'sec-gpc: 1' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36' \
-H 'x-deployment-id: dpl_H6W1h1TT5ymtymWfaTwP8imidC43'
HTTP/2 404
accept-ranges: bytes
access-control-allow-origin: *
age: 13455
cache-control: public, max-age=0, must-revalidate
content-disposition: inline; filename="404"
content-type: text/html; charset=utf-8
date: Wed, 12 Nov 2025 21:14:18 GMT
etag: "48af11d0fb7ae9a3c3a9255880de0319"
last-modified: Wed, 12 Nov 2025 17:30:03 GMT
referrer-policy: origin-when-cross-origin
server: Vercel
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-content-type-options: nosniff
x-current-path: /sign-up/
x-dns-prefetch-control: on
x-frame-options: SAMEORIGIN
x-matched-path: /404
x-vercel-cache: HIT
x-vercel-id: pdx1::6cgst-1762982058905-f56ab7a0c2a7
x-xss-protection: 1; mode=block
content-length: 85710
Request to remote URL hosted on Vercel removing next-router-prefetch: 1 or next-router-segment-prefetch: /_tree header:
curl -IL --max-redirs 1 'https://www.example.com/sign-up/?_rsc=7dz3b'\
-H 'accept: */*' \
-H 'accept-language: en-US,en;q=0.8' \
-H 'next-router-segment-prefetch: /_tree' \
-H 'next-url: /sign-in' \
-H 'priority: i' \
-H 'referer: https://www.example.com/sign-in/' \
-H 'rsc: 1' \
-H 'sec-ch-ua: "Chromium";v="142", "Brave";v="142", "Not_A Brand";v="99"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "macOS"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: same-origin' \
-H 'sec-gpc: 1' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36' \
-H 'x-deployment-id: dpl_H6W1h1TT5ymtymWfaTwP8imidC43'
HTTP/2 200
age: 0
cache-control: private, no-cache, no-store, max-age=0, must-revalidate
content-type: text/x-component
date: Wed, 12 Nov 2025 21:16:15 GMT
referrer-policy: origin-when-cross-origin
server: Vercel
strict-transport-security: max-age=63072000; includeSubDomains; preload
vary: rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch
x-content-type-options: nosniff
x-current-path: /sign-up/
x-dns-prefetch-control: on
x-frame-options: SAMEORIGIN
x-matched-path: /sign-up.rsc
x-vercel-cache: MISS
x-vercel-id: pdx1::sfo1::z6x6x-1762982174954-c25c4bb476a6
x-xss-protection: 1; mode=block
I don't know if this is related.....
The internal file copy function only using / as file path separator.
so that, incorrect file copy in Windows build environment.
| Linux(WSL) | Windows |
|---|---|
Also, always fail (404). that's because, incorrect file copy and not exists next/link prefetch link.
opend PR: #86948