Skip to content

fix(@angular/ssr): validate host headers to prevent header-based SSRF [main]#32499

Open
alan-agius4 wants to merge 2 commits intoangular:mainfrom
alan-agius4:security-issue
Open

fix(@angular/ssr): validate host headers to prevent header-based SSRF [main]#32499
alan-agius4 wants to merge 2 commits intoangular:mainfrom
alan-agius4:security-issue

Conversation

@alan-agius4
Copy link
Collaborator

@alan-agius4 alan-agius4 commented Feb 17, 2026

This change introduces strict validation for Host, X-Forwarded-Host, X-Forwarded-Proto, and X-Forwarded-Port headers in the Angular SSR request handling pipeline, including CommonEngine and AngularAppEngine.

Previously, the application engine constructed the base URL for server-side rendering using these headers without validation. This could allow an attacker to manipulate the headers to steer relative HttpClient requests to arbitrary internal or external hosts (SSRF).

With this change:

  • The Host and X-Forwarded-Host headers are validated against a strict allowlist.
  • localhost and loopback addresses (e.g., 127.0.0.1, [::1]) are allowed by default.
  • X-Forwarded-Port must be numeric.
  • X-Forwarded-Proto must be http or https.
  • Requests with invalid or disallowed headers will now be rejected with a 400 Bad Request status code.

BREAKING CHANGE:

Server-side requests will now fail with a 400 Bad Request error if the Host header does not match a customized allowlist (or localhost).

AngularAppEngine Users:
To resolve this, you must configure the allowedHosts option in your angular.json to include all domain names where your application is deployed.

Example configuration in angular.json:

"architect": {
  "build": {
    "options": {
      "security": {
        "allowedHosts": ["example.com", "*.trusted-example.com"]
      }
    }
  }
}

CommonEngine Users:
If you are using CommonEngine, you must now provide the allowedHosts option when initializing or rendering your application.

Example:

const commonEngine = new CommonEngine({
  allowedHosts: [“example.com”, *.trusted-example.com"]
});

@angular-robot angular-robot bot added detected: breaking change PR contains a commit with a breaking change area: @angular/ssr labels Feb 17, 2026
@alan-agius4 alan-agius4 force-pushed the security-issue branch 3 times, most recently from 07d009a to 26e57a6 Compare February 17, 2026 09:04
@alan-agius4 alan-agius4 added the target: patch This PR is targeted for the next patch release label Feb 17, 2026
@alan-agius4 alan-agius4 added target: minor This PR is targeted for the next minor release and removed target: patch This PR is targeted for the next patch release labels Feb 17, 2026
@alan-agius4 alan-agius4 force-pushed the security-issue branch 3 times, most recently from ecd6230 to 1651898 Compare February 17, 2026 09:46
@alan-agius4 alan-agius4 added the action: review The PR is still awaiting reviews from at least one requested reviewer label Feb 17, 2026
@alan-agius4 alan-agius4 marked this pull request as ready for review February 17, 2026 10:40
@alan-agius4 alan-agius4 changed the title fix(@angular/ssr): validate host headers to prevent header-based SSRF fix(@angular/ssr): validate host headers to prevent header-based SSRF [main] Feb 17, 2026
This change introduces strict validation for `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto`, and `X-Forwarded-Port` headers in the Angular SSR request handling pipeline, including `CommonEngine` and `AngularAppEngine`.

Previously, the application engine constructed the base URL for server-side rendering using these headers without validation. This could allow an attacker to manipulate the headers to steer relative `HttpClient` requests to arbitrary internal or external hosts (SSRF).

With this change:
- The `Host` and `X-Forwarded-Host` headers are validated against a strict allowlist.
- `localhost` and loopback addresses (e.g., `127.0.0.1`, `[::1]`) are allowed by default.
- `X-Forwarded-Port` must be numeric.
- `X-Forwarded-Proto` must be `http` or `https`.
- Requests with invalid or disallowed headers will now be rejected with a `400 Bad Request` status code.

BREAKING CHANGE:

Server-side requests will now fail with a `400 Bad Request` error if the `Host` header does not match a customized allowlist (or localhost).

**AngularAppEngine Users:**
To resolve this, you must configure the `allowedHosts` option in your `angular.json` to include all domain names where your application is deployed.

Example configuration in `angular.json`:

```json
"architect": {
  "build": {
    "options": {
      "security": {
        "allowedHosts": ["example.com", "*.trusted-example.com"]
      }
    }
  }
}
```

**CommonEngine Users:**
If you are using `CommonEngine`,  you must now provide the `allowedHosts` option when initializing or rendering your application.

Example:
```typescript
const commonEngine = new CommonEngine({
  allowedHosts: [“example.com”, “*.trusted-example.com"]
});
```
Copy link
Collaborator

@dgp1130 dgp1130 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a handful of suggestions. Don't worry about backporting anything not critical to your other PRs, I'm less concerned about maintainability, documentation, etc. aspects there, as long as main is in a good state going forward.

Also feel free to skip or come back to the less important comments in future PRs, no need to block this on more than we need to.

export default {
basePath: '${basePath}',
allowedHosts: ${JSON.stringify(
allowedHosts.map((host) => host.replace(/^www\./i, '')),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Do we also need to include loopback addresses like we have in CommonEngine, or is that already handled separately?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already handled. (check the constructor)

Comment on lines 130 to 131
'\n\nAction Required: Update your "angular.json" to include this hostname. ' +
'Path: "projects.[project-name].architect.build.options.security.allowedHosts".';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This is implicitly stating that value is an acceptable host, but it may be correctly blocked as malicious and we wouldn't want to guide users to blindly allowing any hosts which try to connect.

We should suggest this only if value is an expected host. Maybe something like:

Header "${headerName}" with value "${value}" is not allowed.

Possible Action Required: If your server is intended to be hosted at "${value}", update your "angular.json" to include this hostname. Path: "projects.[project-name].architect.build.options.security.allowedHosts". If your server is not hosted at "${value}", then this error can be safely ignored.

Also can we link to any docs for allowedHosts once that lands (maybe after this PR)?

@alan-agius4 alan-agius4 force-pushed the security-issue branch 4 times, most recently from bcfa19a to 14be1c9 Compare February 18, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: review The PR is still awaiting reviews from at least one requested reviewer area: @angular/ssr detected: breaking change PR contains a commit with a breaking change target: minor This PR is targeted for the next minor release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments