Skip to content

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

Closed

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

Activity

  1. added this to the needsTriage milestone on Jun 26, 2026
  2. self-assigned this
    on Jun 26, 2026
  3. added theissue type on Jun 26, 2026
  4. added 6 commits that reference this issue on Jun 26, 2026
    22da66e
    999a337
    a4f3fd7
    299ab00
    2496604
    d9fb854
  5. added a commit that references this issue on Jun 29, 2026
    5f774f8
  6. angular-automatic-lock-bot commented on Jul 30, 2026

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  7. locked and limited conversation to collaborators on Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions