fix: strip base URL from path in SSR locale redirect#4000
Conversation
The `render:before` redirect middleware derived the route path from
`url.pathname`, which still contains `app.baseURL`, while the locale was
detected from the already base-stripped `event.path`. With a base set this
produced a relative path (e.g. "se-path/about") that vue-router's matcher
rejected ("The Matcher cannot resolve relative paths"), so
`isExistingNuxtRoute` failed and the SSR locale redirect silently bailed.
Derive the path from `event.path` via `parsePath` so it stays an absolute,
base-free, query-free route path. Resolves the TODO in shared/matching.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WalkthroughThis PR fixes a bug in the SSR locale-redirect middleware when Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@specs/issues/3887.spec.ts`:
- Around line 30-31: The redirect assertion is too broad—using
expect(res.headers.get('location')).toContain('/about') can match non-localized
paths; update the assertion to check the exact localized target (use the actual
locale your app uses) by replacing the toContain call on
res.headers.get('location') with a strict assertion against the full localized
path (e.g., expect(res.headers.get('location')).toBe('/en/about') or a regex
that asserts the locale prefix like
expect(res.headers.get('location')).toMatch(/^\/en\/about(\?|$)/)), keeping
references to res and headers.get('location') so the test verifies the localized
redirect explicitly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 212bb3d5-2502-489b-b2cf-63087fee9048
📒 Files selected for processing (2)
specs/issues/3887.spec.tssrc/runtime/server/plugin.ts
| expect(res.status).toBe(302) | ||
| expect(res.headers.get('location')).toContain('/about') |
There was a problem hiding this comment.
Strengthen the redirect assertion to avoid false positives.
Line 31 can pass on non-localized redirects (e.g., /base-path/about). Assert the localized target explicitly.
Suggested test tightening
- expect(res.headers.get('location')).toContain('/about')
+ expect(res.headers.get('location')).toBe('/en/about')📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| expect(res.status).toBe(302) | |
| expect(res.headers.get('location')).toContain('/about') | |
| expect(res.status).toBe(302) | |
| expect(res.headers.get('location')).toBe('/en/about') |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@specs/issues/3887.spec.ts` around lines 30 - 31, The redirect assertion is
too broad—using expect(res.headers.get('location')).toContain('/about') can
match non-localized paths; update the assertion to check the exact localized
target (use the actual locale your app uses) by replacing the toContain call on
res.headers.get('location') with a strict assertion against the full localized
path (e.g., expect(res.headers.get('location')).toBe('/en/about') or a regex
that asserts the locale prefix like
expect(res.headers.get('location')).toMatch(/^\/en\/about(\?|$)/)), keeping
references to res and headers.get('location') so the test verifies the localized
redirect explicitly.
🔗 Linked issue
Resolves #3887 (duplicate: #3999)
❓ Type of change
📚 Description
When
app.baseURLis set together withstrategy: 'prefix', the SSR locale-redirect middleware logs:Root cause — in
src/runtime/server/plugin.ts(render:beforehook), the routepathwas derived fromurl.pathname, which still containsapp.baseURL, while the locale was detected fromevent.path, which Nitro has already stripped of the base:That relative string reaches
isExistingNuxtRoute(path)->matcher.resolve({ path }), which vue-router rejects (it only accepts absolute paths). The match then fails and the redirect silently bails — so an unprefixed path under a base is never localized.Fix — derive the path from
event.path(already base-stripped) and run it throughparsePathto drop any query string, keepingpathan absolute, base-free route path. This also resolves the existing TODO insrc/runtime/shared/matching.ts:// TODO: path should not have base url - check if base url is stripped earlier🧪 Test
Added
specs/issues/3887.spec.ts: withapp.baseURL: '/base-path'+strategy: 'prefix', an unprefixed path now redirects to its localized route.GET /base-path/about->302(locationcontains/about) ✅404(redirect bailed) — so the test fails on the unpatched code, confirming it's a real regression test.Checklist
Summary by CodeRabbit
Bug Fixes
app.baseURLis configured with i18n prefix strategy.Tests