Skip to content

[@angular/ssr] RESPONSE_INIT headers are ignored during redirects returned by UrlTree guards #33473

Description

@jakubsobel

Command

serve, build

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

In our application, we need to issue 301 Moved Permanently redirects using a UrlTree inside a canActivate guard. However, we also need to attach custom Cache-Control headers to prevent the highly aggressive, permanent caching that browsers typically apply to 301s.

By default, returning a UrlTree triggers a 302 redirect. We successfully modified the status code to 301 by injecting RESPONSE_INIT and mutating the status:

responseInit.status = 301; // This works perfectly

However, when we attempt to add custom headers to the redirect response using the same RESPONSE_INIT object, they are completely ignored in the final response:

responseInit.headers = { 'X-Test-Header': 'Qwe12345' }; // This is ignored

I dug into the angular-cli source code and found the likely culprit in @angular/ssr.

In packages/angular/ssr/src/app.ts, when handling redirects, the status is correctly extracted from responseInit, but the headers are passed from the matchedRoute instead of picking up or merging the headers defined in responseInit.

Line 356 in app.ts (commit 010cef6):
return createRedirectResponse(result.redirectTo, responseInit.status, headers);
(Notice responseInit.headers is not used here).

In my opinion, if modifying responseInit.headers during a guard execution that returns a UrlTree, those headers should be merged into or applied to the resulting redirect response, just as responseInit.status is.

I am opening this to ask if this is an undocumented, intended behavior or a bug? If it is intended, are there plans to support modifying/adding redirect headers in the future? If it's a bug, I'd love to see it fixed so we can properly manage our cache headers on 301s. Thanks!

(I will provide minimal reproduction repository if necessary, but it's very easy to reproduce on a new empty project using the steps below)

Minimal Reproduction

  1. Set up an Angular SSR app with a child route.
  2. Add a canActivate guard to the route that returns a UrlTree pointing back to home.
  3. In the guard, inject RESPONSE_INIT.
  4. Mutate it: responseInit.status = 301 and responseInit.headers = { 'X-Test-Header': 'Qwe12345' }.
  5. Run the SSR server and request the child route.
  6. Observe the network tab: The response is a 301, but X-Test-Header is missing.

Exception or Error


Your Environment

_                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI       : 22.0.4
Angular           : 22.0.2
Node.js           : 24.18.0
Package Manager   : npm 11.16.0
Operating System  : darwin arm64

┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package                   │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build            │ 22.0.4            │ ^22.0.4           │
│ @angular/cli              │ 22.0.4            │ ^22.0.4           │
│ @angular/common           │ 22.0.2            │ ^22.0.0           │
│ @angular/compiler         │ 22.0.2            │ ^22.0.0           │
│ @angular/compiler-cli     │ 22.0.2            │ ^22.0.0           │
│ @angular/core             │ 22.0.2            │ ^22.0.0           │
│ @angular/forms            │ 22.0.2            │ ^22.0.0           │
│ @angular/platform-browser │ 22.0.2            │ ^22.0.0           │
│ @angular/platform-server  │ 22.0.2            │ ^22.0.0           │
│ @angular/router           │ 22.0.2            │ ^22.0.0           │
│ @angular/ssr              │ 22.0.4            │ ^22.0.4           │
│ rxjs                      │ 7.8.2             │ ~7.8.0            │
│ typescript                │ 6.0.3             │ ~6.0.2            │
│ vitest                    │ 4.1.9             │ ^4.0.8            │
└───────────────────────────┴───────────────────┴───────────────────┘

Anything else relevant?

No response

Metadata

Metadata

Assignees

Type

No fields configured for Bug.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions