[Start] Bare basepath URL (e.g. /preview) redirects to /preview/ on SSR even with trailingSlash: 'never'
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.1k
- Forks
- 1.9k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 143
Description
Discussed in https://github.com/TanStack/router/discussions/7282
Originally posted by Nandan1996 April 28, 2026
Summary
When basepath is set on the router (e.g. /preview) and a user visits the bare basepath URL (http://localhost:3000/preview), the SSR handler issues a redirect to http://localhost:3000/preview/ (with trailing slash). This happens even though trailingSlash defaults to "never".
Visiting / with no basepath does not redirect to /-with-anything, so the behavior is asymmetric.
Where it happens
In packages/router-core/src/router.ts beforeLoad, on the server, the router rebuilds the location and compares publicHref:
if (isServer ?? this.isServer) {
const nextLocation = this.buildLocation({
to: this.latestLocation.pathname,
search: true, params: true, hash: true, state: true,
_includeValidateSearch: true,
})
if (this.latestLocation.publicHref !== nextLocation.publicHref) {
const href = this.getParsedLocationHref(nextLocation)
if (nextLocation.external) throw redirect({ href })
else throw redirect({ href, _builtLocation: nextLocation })
}
}
Walkthrough for request GET /preview with basepath: "/preview":
- Basepath is stripped → internal
latestLocation.pathname=/. buildLocation({ to: "/" })re-joins basepath +/→ produces/preview/.- Incoming
publicHref=/preview, rebuiltpublicHref=/preview/→ mismatch → 308 redirect.
Root case (no basepath): both incoming and rebuilt are /, so no redirect. That is why root "just works" but a bare basepath does not.
Expected
With trailingSlash: "never" (the default), GET /preview should be served as the canonical URL — no redirect to /preview/.
Repro
- TanStack Start app
- Set
basepath: "/preview"in the router config (and matching dev server base/routerBasepath) - Visit
http://localhost:3000/preview→ 308 →http://localhost:3000/preview/
Related
- #4105 — similar shape (visiting
/redirects to/basepath/), but the bare-basepath case here looks distinct.
Question
Is this intentional canonicalization (treat bare basepath as non-canonical), or a bug where trailingSlash is not applied to the rebuilt publicHref before the equality check?
Versions: @tanstack/react-router@1.167.5, @tanstack/react-start@1.166.18.
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 in packages/router-core/src/router.ts at the server-side beforeLoad logic that rebuilds the location and compares publicHref. Reproduce GET /preview with basepath "/preview" and trailingSlash set to "never", then verify the canonical bare-basepath URL is served without redirecting to /preview/.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- full-stack, web-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100