Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fix-ssr-bare-basepath-redirect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@tanstack/router-core': patch
---

fix(router): apply trailingSlash config to the rewritten basepath URL before the SSR redirect comparison.

Fixes [#7291](https://github.com/TanStack/router/issues/7291): visiting a bare basepath URL (e.g. `/preview`) on the server always triggered a spurious `308` redirect to `/preview/` even when `trailingSlash` was `'never'` (the default), because the rewritten public href was synthesised from `URL` parsing — which always normalises to a trailing slash — and never reconciled with the user's `trailingSlash` config before being compared to the incoming request URL.
18 changes: 17 additions & 1 deletion packages/router-core/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2020,8 +2020,24 @@ export class RouterCore<
publicHref = rewrittenUrl.href
external = true
} else {
// Apply trailingSlash config to the rewritten pathname so that the
// publicHref is canonical before it is compared to the incoming URL
// in the SSR redirect check. Without this, visiting the bare basepath
// (e.g. /preview) always produces publicHref = /preview/ from the
// rewrite output and triggers a spurious 308 redirect even when
// trailingSlash is 'never'. See: https://github.com/TanStack/router/issues/7291
const trailingSlashOpt = this.options.trailingSlash ?? 'never'
let rewrittenPathname = rewrittenUrl.pathname
if (trailingSlashOpt === 'never') {
rewrittenPathname = trimPathRight(rewrittenPathname)
} else if (
trailingSlashOpt === 'always' &&
!rewrittenPathname.endsWith('/')
) {
rewrittenPathname += '/'
}
publicHref =
rewrittenUrl.pathname + rewrittenUrl.search + rewrittenUrl.hash
rewrittenPathname + rewrittenUrl.search + rewrittenUrl.hash
}
} else {
// Fast path: no rewrite, skip URL construction entirely
Expand Down