Skip to content

fix: merge incoming request query params with redirect destination query params#12627

Closed
MattieTK wants to merge 1 commit intomainfrom
fix/assets-redirect-query-merge
Closed

fix: merge incoming request query params with redirect destination query params#12627
MattieTK wants to merge 1 commit intomainfrom
fix/assets-redirect-query-merge

Conversation

@MattieTK
Copy link
Copy Markdown
Member

@MattieTK MattieTK commented Feb 21, 2026

Summary

  • Merges incoming request query parameters with redirect destination query parameters in the static assets layer
  • Adds a mergeSearchParams helper that combines both sets of params, with destination params taking precedence
  • Adds test coverage for query string preservation during redirects

Behaviour change

Given a _redirects rule:

/products/:code/:name /products?code=:code&name=:name

A request to /products/42/widget?tracking=abc currently produces:

Location: /products?code=42&name=widget

The tracking=abc parameter from the incoming request is not carried through. The existing logic at handler.ts:1035 uses destination.search || search, which selects one or the other rather than merging.

After this change

Location: /products?tracking=abc&code=42&name=widget

Incoming params are merged in, with destination params taking precedence for duplicate keys.

Prior art

Netlify's _redirects format documents this merging behaviour. From their redirect options docs:

While our service automatically passes on all query string parameters to destination paths for redirects with 200, 301, and 302 HTTP status, you can also choose to define a redirect path based on a specific parameter or combination of parameters.

Our docs for Pages redirects and Workers static assets redirects include the /products/:code/:name /products?code=:code&name=:name example, where this behaviour is relevant. A follow-up docs PR could document the expected query string handling.

Changes

  • packages/workers-shared/asset-worker/src/handler.ts – Added mergeSearchParams() helper, replaced the ||/ternary logic with a call to it
  • packages/workers-shared/asset-worker/tests/handler.test.ts – Added 5 tests covering query string preservation on static redirects, dynamic redirects, param merging with precedence, and cross-origin redirects

Open with Devin

…ery params

When a _redirects rule's destination contained query parameters, any
additional query parameters from the incoming request were dropped. The
existing logic used an either/or choice (destination.search || search)
rather than merging both sets of parameters.

This adds a mergeSearchParams helper that combines both sets, with
destination parameters taking precedence when the same key exists in
both.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 21, 2026

🦋 Changeset detected

Latest commit: ba0c434

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Feb 21, 2026

Claude finished @MattieTK's task —— View job


Changeset Review

Tasks

  • Read changeset guidelines
  • Review changeset file
  • Check attached image
  • Validate changeset format and content
  • Post findings

All changesets look good

The changeset .changeset/fix-redirect-query-merge.md passes all validation criteria:

  1. Version Type: ✅ Correctly marked as patch (bug fix)
  2. Changelog Quality: ✅ Clear description with good explanation of the previous behavior and the fix
  3. Markdown Headers: ✅ No h1/h2/h3 headers present
  4. Analytics: ✅ N/A - no analytics changes
  5. Dependabot: ✅ N/A - not a dependency update
  6. Experimental features: ✅ N/A - not an experimental feature

The changeset follows the format guidelines with a clear imperative title and a well-written body that explains both the problem and the solution.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Feb 21, 2026

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@12627

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@12627

miniflare

npm i https://pkg.pr.new/miniflare@12627

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@12627

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@12627

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@12627

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@12627

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@12627

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@12627

wrangler

npm i https://pkg.pr.new/wrangler@12627

commit: ba0c434

@MattieTK MattieTK marked this pull request as ready for review February 21, 2026 16:06
@MattieTK MattieTK requested review from a team as code owners February 21, 2026 16:06
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

@vicb
Copy link
Copy Markdown
Contributor

vicb commented Feb 22, 2026

The PR template on this repo has:

  • Tests

    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation

    • Cloudflare docs PR(s):
    • Documentation not necessary because:

    Could you please add this to the PR description and fill it out?
    Especially interested in the doc pat: is there a doc PR pending?

Comment on lines +999 to +1009
/**
* Merge query parameters from the incoming request with those from the
* redirect destination. Destination params take precedence – if the same
* key appears in both, the destination value wins.
*
* Returns the merged query string (including the leading `?`), or an
* empty string if there are no params.
*/
const mergeSearchParams = (
incomingSearch: string,
destinationSearch: string
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make this more generic by removing the concept of incoming and destination. I don't think they are menaingfull here. The only thing we need to know if is A or B has higher priority.

Please add JSDonc for both the parameters @param and @return value. The comment is missing the parameter format

Comment on lines +1011 to +1013
if (!incomingSearch && !destinationSearch) {
return "";
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covered by the 2 following ifs

Suggested change
if (!incomingSearch && !destinationSearch) {
return "";
}


// Destination params overwrite incoming params
for (const [key, value] of destination) {
merged.set(key, value);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this handles multiple values (i.e. .getAll()) correctly.

What this code should do:

  • document what we want to do with multiples values (i.e. merge or replace)
  • add tests
  • fix the implementation

@petebacondarwin
Copy link
Copy Markdown
Contributor

Is this actually the desired behaviour? If I understand Netlify's docs, it doesn't merge parameters between the two; it looks like it only copies over params if none are on the source URL of the redirect. Otherwise, if there is any param in the source, it will not match if there are other params. and the user has to construct the destination URL from the captured params.

@MattieTK
Copy link
Copy Markdown
Member Author

Sorry folks, I'll set this back to draft and review this.

There is something here I'm pretty sure, I've just probably missed the mark.

@MattieTK MattieTK marked this pull request as draft February 23, 2026 09:44
@MattieTK MattieTK closed this Feb 23, 2026
@github-project-automation github-project-automation Bot moved this from Untriaged to Done in workers-sdk Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants