fix(vercel): handle isr requests with `passQuery: true`
Resolves #1880, #3594, #3651
Normally, with ISR matched routes, there is an (undocumented/legacy) x-now-route-matches header that contains the full pathname of the matched route. And Nitro vercel entry uses this header value to restore the original URL, but when passQuery isr route rule config is set, this header won't exist anymore, and the path is invalid (isr function name). We cannot always assume url query param is for ISR functions (it can be an actual query in normal routes!)
This PR changes the internal url query param to __isr_route and, if it exists, also attempts to rewrite without relying on legacy header but instead using a runtime protection check to make sure the requested route matches ISR route patterns.
We also make sure internal "__isr_route" is added to passQuery (if specified)
Beta testing:
package.json:
"resolutions": {
"nitropack": "https://pkg.pr.new/nitrojs/nitro/nitropack@3539"
},
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| nitro.build | Preview | Comment | Dec 9, 2025 8:50pm |
hey @pi0, do you plan to release the fix in upcoming version maybe?
Would love to see this merged. Using
"resolutions": {
"nitropack": "https://pkg.pr.new/nitrojs/nitro/nitropack@3539"
}
works but is not ideal.
The problem I face with https://pkg.pr.new/nitrojs/nitro/nitropack@3539: Building and starting the application with node-presets causes an issue with the public folder resolution in .output
Reproduction: https://github.com/fmoessle/nitro-vercel-isr/tree/test-3539-fix
pnpm build
pnpm preview
results in errors like this:
request error] [unhandled] [GET] http://[::]:3000/favicon.ico
H3Error: ENOENT: no such file or directory, open 'XXX/florian/projects/test/nitro-vercel-isr/.output/server/chunks/public/favicon.ico'
at async open (node:internal/fs/promises:634:25)
... 2 lines matching cause stack trace ...
at async Server.toNodeHandle (file:///XXX/.output/server/chunks/nitro/nitro.mjs:1917:7) {
cause: Error: ENOENT: no such file or directory, open 'XXX/.output/server/chunks/public/favicon.ico'
at async open (node:internal/fs/promises:634:25)
at async Object.readFile (node:internal/fs/promises:1238:14)
at async Object.handler (file:///XXX/.output/server/chunks/nitro/nitro.mjs:1646:19)
at async Server.toNodeHandle (file:///XXX/.output/server/chunks/nitro/nitro.mjs:1917:7) {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: 'XXX/.output/server/chunks/public/favicon.ico'
},
statusCode: 500,
fatal: false,
unhandled: true,
statusMessage: undefined,
data: undefined
}
There is no public folder in .output/server/chunks
๐ Walkthrough
Walkthrough
The PR introduces a new constant ISR_URL_PARAM with value "__isr_route" to standardize Incremental Static Regeneration parameter handling across the Vercel preset. The constant replaces hardcoded parameter references in route matching, URL query handling, and test fixtures for consistent ISR routing behavior.
Changes
| Cohort / File(s) | Summary |
|---|---|
New constant definition src/presets/vercel/runtime/consts.ts |
Introduces ISR_URL_PARAM constant with value "__isr_route" for centralized ISR parameter naming. |
Runtime ISR flow updates src/presets/vercel/runtime/vercel.ts |
Refactors ISR detection and routing logic to extract ISR_URL_PARAM from headers or query parameters; adds route rules lookup via getRouteRulesForPath() and implements withQuery() for preserving non-ISR query parameters. |
Utility and configuration src/presets/vercel/utils.ts |
Replaces hardcoded "url" parameter with ISR_URL_PARAM constant in route regex captures, query destinations, and prerender configuration allowlist. |
Test updates test/presets/vercel.test.ts |
Updates ISR route assertions to use __isr_route in destination query strings and source regex named capture groups; adds "__isr_route" to prerender config allowQuery array. |
Estimated code review effort
๐ฏ 3 (Moderate) | โฑ๏ธ ~20 minutes
src/presets/vercel/runtime/vercel.ts: Carefully verify the two-path ISR detection flow (header-based vs. passQuery-based) correctly handles URL parameter extraction and route rule resolution.src/presets/vercel/utils.ts: Confirm all hardcoded"url"parameter references have been consistently replaced withISR_URL_PARAMacross regex patterns and prerender config generation.- Cross-file consistency: Ensure the constant definition, usage in runtime logic, utility transformations, and test expectations are properly aligned throughout the preset.
Pre-merge checks and finishing touches
โ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description check | โ Passed | The description is well-structured, relates to the changeset, references multiple linked issues, explains the problem and solution clearly. |
| Linked Issues check | โ Passed | The PR addresses issue #1880 (and others) by introducing __isr_route parameter to handle ISR requests with passQuery, ensuring query parameters are preserved in Vercel's caching behavior. |
| Out of Scope Changes check | โ Passed | All changes are directly scoped to fixing ISR request handling with passQuery support; changes to consts, runtime, utils, and tests are cohesively focused on this objective. |
| Docstring Coverage | โ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
| Title check | โ Passed | The title follows conventional commits format with type 'fix', scope 'vercel', and subject describing the change. |
โจ Finishing touches
- [ ] ๐ Generate docstrings
๐งช Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
- [ ] Commit unit tests in branch
fix/vercel-isr-url
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
@coderabbitai review
โ Actions performed
Review triggered.
Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.
@fmoessle I couldn't reproduce error on your branch... This PR is also only affecting vercel preset not sure how can it break node-server. If you continue to having them on nightly or next release please feel free to raise an issue with more steps for reproduction.