Skip to content

fix(build): throw error if there is _next folder inside the public folder - #2814

Open
NriotHrreion wants to merge 3 commits into
cloudflare:mainfrom
NriotHrreion:fix/issue-2778-next-static-served-ahead-of-middleware
Open

fix(build): throw error if there is _next folder inside the public folder#2814
NriotHrreion wants to merge 3 commits into
cloudflare:mainfrom
NriotHrreion:fix/issue-2778-next-static-served-ahead-of-middleware

Conversation

@NriotHrreion

@NriotHrreion NriotHrreion commented Aug 5, 2026

Copy link
Copy Markdown

Closes #2778

Root cause

In packages/vinext/src/server/prod-server.ts:1441, the function resolveAppRouterAssetPath() does not consider the case where the provided pathname that starts with _next/static is from public folder.

// No `assetPrefix` (default layout), or absolute-URL `assetPrefix` with no
// path component — both land files on disk at `dist/client/_next/static/...`
// and emit URLs starting `/_next/static/...`.
if (pathname.startsWith(nextStaticDir)) return pathname;

But in the upstream next.js source, it is not allowed to put a _next folder inside the public folder, or it will throw an error during npm run build.

What changed

I created an assert function assertNoPublicDirAssetConflict(). This function checks the paths. Once it found there is any _next inside public folder, it will throw an error and interrupt the build.

This assert function is executed during configResolved() (in packages/vinext/src/index.ts):

async configResolved(config) {
+  const publicDir = config.publicDir === "" ? null : config.publicDir;
+  const assetsDir =
+    config.environments.client.build.assetsDir ??
+    config.build.assetsDir ??
+    resolveAssetsDir(nextConfig.assetPrefix ?? "");
+
+  assertNoPublicDirAssetConflict({
+    root: config.root,
+    publicDir,
+    assetsDir,
+  });

  // ...

The workaround is quite simple and is aligned with the behavior of upstream next.js.

Testing

  • pnpm test tests/public-dir-conflict.test.ts

Notes

The error message is as following:

const PUBLIC_NEXT_CONFLICT_ERROR =
  "You can not have a '_next' folder inside of your public folder. " +
  "This conflicts with the internal '/_next' route. " +
  "https://nextjs.org/docs/messages/public-next-folder-conflict";

I'm not sure if the link should be replaced with vinext's one.

Thanks for reviewing!

…folder

The `_next` folder under the public folder conflicts
with the internal `_next` route, which is not expected
@NriotHrreion

Copy link
Copy Markdown
Author

Oops, it seems that my formatter just replaced all import relative paths with import alias...

@pkg-pr-new

pkg-pr-new Bot commented Aug 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2814
npm i https://pkg.pr.new/create-vinext-app@2814
npm i https://pkg.pr.new/@vinext/types@2814
npm i https://pkg.pr.new/vinext@2814

commit: bbbcbc5

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared bbbcbc5 against base 3c5cea4 using alternating same-runner rounds. Next.js was unchanged and skipped.

1 improved · 0 regressed · 5 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 134.6 KB 134.6 KB ⚫ -0.0%
Client entry size (gzip) vinext 122.1 KB 122.2 KB ⚫ +0.0%
Dev server cold start vinext 3.11 s 3.01 s 🟢 -3.3%
Production build time vinext 3.37 s 3.33 s ⚫ -1.2%
RSC entry closure size (gzip) vinext 113.8 KB 113.9 KB ⚫ +0.0%
Server bundle size (gzip) vinext 191.4 KB 191.4 KB ⚫ +0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Files under public/_next/static/ are served as build assets, ahead of middleware

2 participants