vercel / vercel/next.js

URL search parameters including spaces are duplicated after middleware rewrite

Open
#79,375 4 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

linear: next Middleware
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 behaviour button
  • 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.