URL search parameters including spaces are duplicated after middleware rewrite
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.5k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://github.com/federicobadini/search-param-bug-repro
To Reproduce
When using Next.js middleware to perform a rewrite, URL search parameters containing spaces (encoded as +) result in duplicated query parameters—one with the expected value, and one with an empty string.
To replicate the bug simply:
- run the minimal reproduction
- check the default behaviour by clicking on the
Trigger buggy behaviourbutton - check the workaround via
Trigger fixed behaviour
If you prefer, the minimal repro is already deployed on Vercel here: https://search-param-bug-repro.vercel.app/
Current vs. Expected behavior
When a request with a query string containing + (e.g., ?q=hello+world) hits a middleware that rewrites the path, the resulting request seems to reach the page with two parameters instead of just one:
q=hello world ✅ (decoded correctly)
q="" ❌ (duplicate with empty value)
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 24.4.0: Fri Apr 11 18:33:47 PDT 2025; root:xnu-11417.101.15~117/RELEASE_ARM64_T6000
Available memory (MB): 32768
Available CPU cores: 10
Binaries:
Node: 20.18.2
npm: 10.8.2
Yarn: 1.22.19
pnpm: N/A
Relevant Packages:
next: 15.2.3 // There is a newer version (15.3.2) available, upgrade recommended!
eslint-config-next: 15.3.2
react: 19.1.0
react-dom: 19.1.0
typescript: 5.8.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Middleware
Which stage(s) are affected? (Select all that apply)
Vercel (Deployed)
Additional context
The suggested workaround - manually re-encoding the search parameters using encodeURIComponent() before the middleware rewrite - was discovered after extensive debugging. This approach appears to prevent the issue entirely.
It seems likely that Next.js performs some internal handling of the query string during the rewrite process, and may be misinterpreting + at some stage, resulting in duplicated parameters.
The minimal reproduction highlights this clearly. When request.nextUrl is passed directly to rewrite(), the bug occurs. However, if the URL is reconstructed manually with properly encoded parameters, everything works as expected.
request.nextUrl.pathname = rewritePathTarget;
if (useBuggyMethod) {
// NO REWRITE - Buggy
console.log("Search params unchanged", request.nextUrl.search);
} else {
// EXPLICIT REWRITE via encodeURIComponent - Working
let customSearchString = "";
const paramsArray = [];
for (const [key, value] of searchParams.entries()) {
paramsArray.push(
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
);
}
if (paramsArray.length > 0) {
customSearchString = `?${paramsArray.join("&")}`;
}
request.nextUrl.search = customSearchString;
console.log(
"Manually built query string with %20 for spaces in keys",
request.nextUrl.search
);
}
return NextResponse.rewrite(request.nextUrl);
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked minimal reproduction and inspect the middleware entry point that calls NextResponse.rewrite(request.nextUrl). Compare the direct rewrite with the manual encodeURIComponent workaround for a query such as ?q=hello+world. Done means the rewritten request reaches the page with one correctly decoded q parameter rather than an empty duplicate.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, next.js
- Domain
- web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100