[PR #217] fix(rewrites): serve static files from public/ when rewrite target is a .html path #398

Open
opened 2026-05-06 12:39:37 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/217
Author: @yunus25jmi1
Created: 3/1/2026
Status: 🔄 Open

Base: mainHead: fix/issue-199-rewrites


📝 Commits (10+)

  • 506d825 fix: serve static HTML files from public/ after rewrites (issue #199)
  • 7d3786f Merge remote-tracking branch 'upstream/main' into fix/issue-199-rewrites
  • cc56d2c address bonk review: hoist resolvedPublicDir, log non-ENOENT errors, add wasm MIME type, document leading-slash assumption
  • c4835ee address bonk review: hoist __publicDir to module scope, add fallback rewrite static file tests
  • 945b9cc fix: use path.resolve for publicDir consistency with index.ts
  • a6852a1 Update packages/vinext/src/index.ts
  • e629695 Update packages/vinext/src/index.ts
  • a9013cd Merge upstream/main into fix/issue-199-rewrites
  • cab6115 fix syntax errors in merge resolution
  • e30bd8b fix: update snapshots and formatting for static file serving feature

📊 Changes

17 files changed (+710 additions, -12 deletions)

View changed files

📝 packages/vinext/src/entries/app-rsc-entry.ts (+57 -0)
📝 packages/vinext/src/index.ts (+76 -3)
packages/vinext/src/server/mime.ts (+47 -0)
📝 packages/vinext/src/server/prod-server.ts (+40 -8)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+289 -1)
📝 tests/app-router.test.ts (+60 -0)
tests/fixtures/app-basic/app/action-dynamic-redirect/page.tsx (+23 -0)
📝 tests/fixtures/app-basic/app/actions/actions.ts (+8 -0)
📝 tests/fixtures/app-basic/next.config.ts (+6 -0)
tests/fixtures/app-basic/public/auth/no-access.html (+9 -0)
tests/fixtures/app-basic/public/fallback-page.html (+10 -0)
tests/fixtures/app-basic/public/static-html-page.html (+10 -0)
📝 tests/fixtures/pages-basic/next.config.mjs (+15 -0)
tests/fixtures/pages-basic/public/auth/no-access.html (+9 -0)
tests/fixtures/pages-basic/public/fallback-page.html (+10 -0)
tests/fixtures/pages-basic/public/static-html-page.html (+10 -0)
📝 tests/pages-router.test.ts (+31 -0)

📄 Description

Fixes #199.

Problem

When next.config rewrites() map a clean URL to a static .html file in public/ (a common pattern for serving pre-built static pages like /auth/no-access/auth/no-access.html), vinext was returning 404 because no Next.js route matches .html paths.

Root Cause

After afterFiles rewrites resolve a path to *.html, the routing logic in all three server paths would find no matching app/pages route for the .html URL and return 404, without ever checking the filesystem's public/ directory.

Fix

Three server paths updated:

  1. Pages Router dev (packages/vinext/src/index.ts): After afterFiles/fallback rewrites, before the final handler() call, check if the resolved URL points to a file in public/ and serve it directly.

  2. App Router dev (packages/vinext/src/server/app-dev-server.ts): The generated RSC entry now checks public/ for the rewritten pathname before rendering the 404 page. Added a new optional root parameter to generateRscEntry() so the public directory path is embedded in the generated virtual module.

  3. Production server (packages/vinext/src/server/prod-server.ts): After afterFiles rewrites and after fallback rewrites produce a path with a file extension, tryServeStatic() is called against the built clientDir (which contains public/ files) before passing to SSR.

Also added a staticMimeType() helper in index.ts to ensure correct Content-Type headers.

Tests

  • Integration tests in app-router.test.ts and pages-router.test.ts: GET /static-html-page (rewritten → /static-html-page.html) returns 200 with correct HTML content and text/html Content-Type.
  • Unit test in app-router.test.ts: generateRscEntry() embeds the public/ path in the generated code.
  • Fixture public/static-html-page.html files added to both app-basic and pages-basic fixtures; next.config rewrites updated.

What async rewrites() flat array does

Per Next.js semantics, when rewrites() returns a flat array, all rules go into afterFiles. vinext already handles this correctly via resolveNextConfig(). This PR fixes the serving of the rewritten destination when it's a static file.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/217 **Author:** [@yunus25jmi1](https://github.com/yunus25jmi1) **Created:** 3/1/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/issue-199-rewrites` --- ### 📝 Commits (10+) - [`506d825`](https://github.com/cloudflare/vinext/commit/506d82567dbacd61e4f4d3f66f1d3cbbddd845d7) fix: serve static HTML files from public/ after rewrites (issue #199) - [`7d3786f`](https://github.com/cloudflare/vinext/commit/7d3786fbc5beb25e1b9448d38e2bd3af9e8fe5b4) Merge remote-tracking branch 'upstream/main' into fix/issue-199-rewrites - [`cc56d2c`](https://github.com/cloudflare/vinext/commit/cc56d2c88376eada0a0ac22b8a194e5a6b6d97e3) address bonk review: hoist resolvedPublicDir, log non-ENOENT errors, add wasm MIME type, document leading-slash assumption - [`c4835ee`](https://github.com/cloudflare/vinext/commit/c4835eedf9b9eaa0128edbdb6c06872e9a6fa2ad) address bonk review: hoist __publicDir to module scope, add fallback rewrite static file tests - [`945b9cc`](https://github.com/cloudflare/vinext/commit/945b9cc3e97a544425ff25acdbc0d8f8fdbc4817) fix: use path.resolve for publicDir consistency with index.ts - [`a6852a1`](https://github.com/cloudflare/vinext/commit/a6852a165da68309b8711de81920bab9529cc042) Update packages/vinext/src/index.ts - [`e629695`](https://github.com/cloudflare/vinext/commit/e6296958665839162cda9b4497af1e0e87dbdfdc) Update packages/vinext/src/index.ts - [`a9013cd`](https://github.com/cloudflare/vinext/commit/a9013cdb60dc624c4b9d23e15b9cc6cf58ff495e) Merge upstream/main into fix/issue-199-rewrites - [`cab6115`](https://github.com/cloudflare/vinext/commit/cab61157330704583a5599b7b1a0618ce95d14dc) fix syntax errors in merge resolution - [`e30bd8b`](https://github.com/cloudflare/vinext/commit/e30bd8b016b630fca7aae5167b8727009ce490dd) fix: update snapshots and formatting for static file serving feature ### 📊 Changes **17 files changed** (+710 additions, -12 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+57 -0) 📝 `packages/vinext/src/index.ts` (+76 -3) ➕ `packages/vinext/src/server/mime.ts` (+47 -0) 📝 `packages/vinext/src/server/prod-server.ts` (+40 -8) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+289 -1) 📝 `tests/app-router.test.ts` (+60 -0) ➕ `tests/fixtures/app-basic/app/action-dynamic-redirect/page.tsx` (+23 -0) 📝 `tests/fixtures/app-basic/app/actions/actions.ts` (+8 -0) 📝 `tests/fixtures/app-basic/next.config.ts` (+6 -0) ➕ `tests/fixtures/app-basic/public/auth/no-access.html` (+9 -0) ➕ `tests/fixtures/app-basic/public/fallback-page.html` (+10 -0) ➕ `tests/fixtures/app-basic/public/static-html-page.html` (+10 -0) 📝 `tests/fixtures/pages-basic/next.config.mjs` (+15 -0) ➕ `tests/fixtures/pages-basic/public/auth/no-access.html` (+9 -0) ➕ `tests/fixtures/pages-basic/public/fallback-page.html` (+10 -0) ➕ `tests/fixtures/pages-basic/public/static-html-page.html` (+10 -0) 📝 `tests/pages-router.test.ts` (+31 -0) </details> ### 📄 Description Fixes #199. ## Problem When `next.config` rewrites() map a clean URL to a static `.html` file in `public/` (a common pattern for serving pre-built static pages like `/auth/no-access` → `/auth/no-access.html`), vinext was returning 404 because no Next.js route matches `.html` paths. ## Root Cause After `afterFiles` rewrites resolve a path to `*.html`, the routing logic in all three server paths would find no matching app/pages route for the `.html` URL and return 404, without ever checking the filesystem's `public/` directory. ## Fix Three server paths updated: 1. **Pages Router dev** (`packages/vinext/src/index.ts`): After afterFiles/fallback rewrites, before the final `handler()` call, check if the resolved URL points to a file in `public/` and serve it directly. 2. **App Router dev** (`packages/vinext/src/server/app-dev-server.ts`): The generated RSC entry now checks `public/` for the rewritten pathname before rendering the 404 page. Added a new optional `root` parameter to `generateRscEntry()` so the public directory path is embedded in the generated virtual module. 3. **Production server** (`packages/vinext/src/server/prod-server.ts`): After afterFiles rewrites and after fallback rewrites produce a path with a file extension, `tryServeStatic()` is called against the built `clientDir` (which contains `public/` files) before passing to SSR. Also added a `staticMimeType()` helper in `index.ts` to ensure correct `Content-Type` headers. ## Tests - Integration tests in `app-router.test.ts` and `pages-router.test.ts`: `GET /static-html-page` (rewritten → `/static-html-page.html`) returns 200 with correct HTML content and `text/html` Content-Type. - Unit test in `app-router.test.ts`: `generateRscEntry()` embeds the `public/` path in the generated code. - Fixture `public/static-html-page.html` files added to both `app-basic` and `pages-basic` fixtures; `next.config` rewrites updated. ## What async rewrites() flat array does Per Next.js semantics, when `rewrites()` returns a flat array, all rules go into `afterFiles`. vinext already handles this correctly via `resolveNextConfig()`. This PR fixes the serving of the rewritten destination when it's a static file. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#398
No description provided.