[workers-shared] `_redirects` targets like `https://host?query` and `https://host#hash` get a trailing slash appended after the query/fragment

Open Beginner friendly
#16,924 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript, wasm
Domain
cli, tooling

Research direction

Start with URL_REGEX and extractPathname in packages/workers-shared/utils/configuration/validateURL.ts, then trace how parseRedirects in parseRedirects.ts passes the target through validateUrl. Run the three-line parseRedirects snippet from the issue to reproduce. Done when https://example.com?utm=1 and https://example.com#section keep their query or fragment without a trailing /, while https://example.com/?utm=1 still parses as before.

Written by the indexing model from the issue text.

Description

bug package:workers-shared
What versions & operating system are you using?

[email protected], main (81e56dd), Node.js v26.13.0, Linux x64

Please provide a link to a minimal reproduction

https://gist.github.com/trackoor/a6e485cf5e6727fa1e3aa1d0313d43ee

Describe the Bug

In packages/workers-shared/utils/configuration/validateURL.ts, validateUrl splits absolute URLs with URL_REGEX = /^https:\/\/+(?<host>[^/]+)\/?(?<path>.*)/. When a _redirects target is an absolute URL with a query string or fragment and no / before it (such as https://example.com?utm=1 or https://example.com#section), (?<host>[^/]+) captures the query string or fragment as part of host, and extractPathname("") appends / after it.

Steps to reproduce with [email protected]:

mkdir -p public
cat > public/_redirects << 'EOF'
/a https://example.com?utm=1
/b https://example.com#section
/c https://example.com/?utm=1
EOF

cat > wrangler.json << 'EOF'
{"name":"repro","compatibility_date":"2026-01-01","assets":{"directory":"./public"}}
EOF

node -e '
const { unstable_dev } = require("wrangler");
(async () => {
  const worker = await unstable_dev(undefined, { config: "wrangler.json", experimental: { disableExperimentalWarning: true } });
  for (const p of ["/a", "/b", "/c"]) {
    const res = await worker.fetch("http://localhost" + p, { redirect: "manual" });
    console.log(p, res.status, res.headers.get("location"));
  }
  await worker.stop();
})();
'

Also directly via parseRedirects on main (81e56dd):

import { parseRedirects } from "./packages/workers-shared/utils/configuration/parseRedirects";

for (const line of [
  "/a https://example.com?utm=1",
  "/b https://example.com#section",
  "/c https://example.com/?utm=1",
]) {
  console.log(line, "->", parseRedirects(line).rules.map((r) => r.to));
}
// /a https://example.com?utm=1 -> [ 'https://example.com?utm=1/' ]
// /b https://example.com#section -> [ 'https://example.com#section/' ]
// /c https://example.com/?utm=1 -> [ 'https://example.com/?utm=1' ]

Actual output from unstable_dev:

/a 302 https://example.com/?utm=1/
/b 302 https://example.com/#section/
/c 302 https://example.com/?utm=1

Expected output:

/a 302 https://example.com/?utm=1
/b 302 https://example.com/#section
/c 302 https://example.com/?utm=1

According to the WHATWG URL Standard, a host ends at /, ?, or #, so https://example.com?utm=1 and https://example.com/?utm=1 have the same host (example.com), pathname (/), and query (?utm=1).

Please provide any relevant error logs

No error or warning is logged ([wrangler:info] ✨ Parsed 3 valid redirect rules.).

Credit: https://ucsbplse.github.io/

Dominant language
TypeScript
Stars
4.7k
Forks
1.6k
Avg merge
1d 23h
Merged PRs (30d)
268

Getting set up

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.

More from cloudflare/workers-sdk

All issues in cloudflare/workers-sdk

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.