Command
other
Is this a regression?
The previous version in which this bug was not present was
No response
Description
When a request matches a route with a string redirectTo, @angular/ssr answers with a 302 built from the route config, without running the router. If the route's own path has more than one segment, a relative redirectTo is resolved against the wrong base.
{ path: 'old/page', redirectTo: 'new/page' }:
- Server:
GET /old/page returns Location: /old/new/page (a 404).
- Router (in-app navigation to
/old/page): /new/page.
The router replaces all segments matched by the redirecting route (old/page). The server only removes the last one (page). Routes whose path is a single segment or empty are not affected, which is probably why this has gone unnoticed.
Note: fixing this changes current behaviour. app_spec.ts asserts the current result (L207-L217: redirect/relative → /redirect/home, the router gives /home), and an app could depend on it if it happens to have a route at the server-computed path.
Minimal Reproduction
npx @angular/cli@22.2.2 new repro-app --ssr --defaults
cd repro-app
src/app/app.routes.ts:
import { Component } from '@angular/core';
import { Routes } from '@angular/router';
@Component({ template: 'New page' })
class NewPage {}
export const routes: Routes = [
{ path: 'old/page', redirectTo: 'new/page' },
{ path: 'new/page', component: NewPage },
];
src/app/app.routes.server.ts (server rendering, so the request reaches the redirect handling instead of a prerendered redirect page):
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [{ path: '**', renderMode: RenderMode.Server }];
npx ng build
NG_ALLOWED_HOSTS=localhost PORT=4000 node dist/repro-app/server/server.mjs
curl -si 'localhost:4000/old/page' | grep -i '^location'
Expected: location: /new/page (where the router navigates). Actual: location: /old/new/page.
Exception or Error
$ curl -si localhost:4000/old/page | head -3
HTTP/1.1 302 Found
X-Powered-By: Express
location: /old/new/page
Your Environment
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI : 22.2.2
Angular : 22.2.2
Node.js : 24.18.0
Package Manager : npm 11.16.0
Operating System : linux x64
┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build │ 22.2.2 │ ^22.2.2 │
│ @angular/cli │ 22.2.2 │ ^22.2.2 │
│ @angular/common │ 22.2.2 │ ^22.2.0 │
│ @angular/compiler │ 22.2.2 │ ^22.2.0 │
│ @angular/compiler-cli │ 22.2.2 │ ^22.2.0 │
│ @angular/core │ 22.2.2 │ ^22.2.0 │
│ @angular/forms │ 22.2.2 │ ^22.2.0 │
│ @angular/platform-browser │ 22.2.2 │ ^22.2.0 │
│ @angular/platform-server │ 22.2.2 │ ^22.2.0 │
│ @angular/router │ 22.2.2 │ ^22.2.0 │
│ @angular/ssr │ 22.2.2 │ ^22.2.2 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 6.0.3 │ ~6.0.2 │
└───────────────────────────┴───────────────────┴───────────────────┘
Anything else relevant?
resolveRedirectTo in ng-routes.ts#L543-L554 removes one segment (segments.pop()), while the router's applyRedirectCommands replaces all segments consumed by the route.
Related: #33504 / #33516 (parameters in the same function).
I have a fix with tests and will open a PR for it. It updates the two app_spec expectations above to the router's result. If the current behaviour is intended, or this should wait for a major, I'm happy to adjust.
Not browser specific. Seen with the default Express server.ts generated by ng new --ssr; the redirect comes from AngularNodeAppEngine.handle(), not from Express itself.
Command
other
Is this a regression?
The previous version in which this bug was not present was
No response
Description
When a request matches a route with a string
redirectTo,@angular/ssranswers with a 302 built from the route config, without running the router. If the route's ownpathhas more than one segment, a relativeredirectTois resolved against the wrong base.{ path: 'old/page', redirectTo: 'new/page' }:GET /old/pagereturnsLocation: /old/new/page(a 404)./old/page):/new/page.The router replaces all segments matched by the redirecting route (
old/page). The server only removes the last one (page). Routes whose path is a single segment or empty are not affected, which is probably why this has gone unnoticed.Note: fixing this changes current behaviour.
app_spec.tsasserts the current result (L207-L217:redirect/relative→/redirect/home, the router gives/home), and an app could depend on it if it happens to have a route at the server-computed path.Minimal Reproduction
npx @angular/cli@22.2.2 new repro-app --ssr --defaults cd repro-appsrc/app/app.routes.ts:src/app/app.routes.server.ts(server rendering, so the request reaches the redirect handling instead of a prerendered redirect page):Expected:
location: /new/page(where the router navigates). Actual:location: /old/new/page.Exception or Error
Your Environment
Anything else relevant?
resolveRedirectToinng-routes.ts#L543-L554removes one segment (segments.pop()), while the router'sapplyRedirectCommandsreplaces all segments consumed by the route.Related: #33504 / #33516 (parameters in the same function).
I have a fix with tests and will open a PR for it. It updates the two
app_specexpectations above to the router's result. If the current behaviour is intended, or this should wait for a major, I'm happy to adjust.Not browser specific. Seen with the default Express
server.tsgenerated byng new --ssr; the redirect comes fromAngularNodeAppEngine.handle(), not from Express itself.