Skip to content

SSR silently falls back to CSR after security patch (GHSA-x288-3778-4hhx) — allowedHosts not mentioned in migration guide #33048

@rjuchn

Description

@rjuchn

Command

update

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

21.1.4

Description

The security patch for GHSA-x288-3778-4hhx introduced a mandatory allowedHosts
configuration for CommonEngine. When it is not configured after upgrading, Angular SSR
silently falls back to client-side rendering — returning a bare <app-root></app-root>
with HTTP 200 and no thrown exception.

Any application upgrading via ng update loses SSR silently with:

  • No build error
  • No test failure
  • No runtime exception
  • The page appears fully functional (Angular bootstraps client-side)

The only signal is a console.error to Node.js stdout that is trivially missed,
especially in containerized environments where Node stdout is mixed with other logs.

ng update has no mention of this requirement, and the migration guide is silent about it.
The only documentation is buried in the security guide, far from any upgrade path.

Minimal Reproduction

  1. Generate a standard SSR app on a version before the security patch (last unaffected: 21.1.4):

    ng new repro --ssr
    cd repro
    
  2. Confirm SSR works on the unaffected version:

    ng build
    node dist/repro/server/server.mjs &
    curl -s http://localhost:4000/ | grep -c "app-root><"
    # Returns 0 — app-root has content, SSR is working
    kill %1
    
  3. Upgrade to a patched version (first affected: 21.1.5):

    ng update @angular/cli@21.1.5 @angular/core@21.1.5 @angular/ssr@21.1.5
    
  4. Build and start the SSR server again — no changes to server.ts:

    ng build
    node dist/repro/server/server.mjs
    
  5. Check the SSR response:

    curl -s http://localhost:4000/ | grep -c "app-root><"
    # Returns 1 — app-root is empty, SSR silently stopped working
    
  6. Check Node.js stdout — you will see:

    ERROR: URL with hostname "localhost" is not allowed. Please provide a list of
    allowed hosts in the "allowedHosts" option in the "CommonEngine" constructor.
    Falling back to client side rendering. This will become a 400 Bad Request in a future major version.
    

    Note: ng update in step 3 produced no warning or action required message about this.

Fix: add allowedHosts to CommonEngine in server.ts:

const commonEngine = new CommonEngine({
  allowedHosts: ['localhost', '*.yourdomain.com'],
});

Verified results

Angular 21.1.4 — SSR works, app-root contains rendered content:

<app-root ng-version="21.1.4" ngh="1">
  <p id="before">Before defer (should always be in SSR output)</p>
  <app-deferred><p>I am inside the defer block</p></app-deferred>
  <p id="after">After defer (should always be in SSR output)</p>
</app-root>

Angular 21.1.5 — SSR silently falls back to CSR after ng update, no changes to server.ts:

ERROR: URL with hostname "localhost" is not allowed.Please provide a list of allowed hosts
in the "allowedHosts" option in the "CommonEngine" constructor.
Falling back to client side rendering. This will become a 400 Bad Request in a future major version.

<app-root></app-root>

ng update produced zero output about this requirement.

Exception or Error

ERROR: URL with hostname "localhost" is not allowed.Please provide a list of allowed hosts
in the "allowedHosts" option in the "CommonEngine" constructor.
Falling back to client side rendering. This will become a 400 Bad Request in a future major version.

Your Environment

Angular CLI: 21.1.5
Angular: 21.1.5
@angular/ssr: 21.1.5
@angular-devkit/build-angular: 21.1.5
Node.js: 20.19.2
Package Manager: npm 10.8.2
OS: darwin arm64
express: 4.22.1

Last working version: @angular/ssr 21.1.4
First broken version: @angular/ssr 21.1.5


Relevant `angular.json` (build options):

"builder": "@angular-devkit/build-angular:application",
"options": {
  "server": "src/main.server.ts",
  "outputMode": "server",
  "ssr": {
    "entry": "server.ts"
  }
}

Anything else relevant?

  • This affects all SSR setups using CommonEngine
  • The affected versions are: >=21.1.5, >=21.2.0-rc.1, >=20.3.17, >=19.2.21 (all patched versions from GHSA-x288-3778-4hhx)
  • @angular/ssr 21.2.7 does not include an ng-update key in package.json — there are zero update migrations provided. Running ng update (or nx migrate) completes silently with no output related to allowedHosts whatsoever.
  • Related issue: #32616 — SSR strict host validation breaks application on cloud platform (closed, different angle — focuses on Kubernetes IP headers, not the missing documentation)

What would help:

  1. Migration guide — add a note that allowedHosts must be configured in server.ts after upgrading, with a link to the security guide. The domains are application-specific so no automation is possible, but a clear callout during upgrade would prevent this.

  2. ng update warning — print an ACTION REQUIRED message during the update process, similar to how other breaking changes are surfaced. Example:

    ACTION REQUIRED: The security patch for GHSA-x288-3778-4hhx requires configuring
    allowedHosts in your server.ts. Without it, SSR will silently fall back to CSR.
    See: https://angular.dev/guide/security#configuring-allowed-hosts
    
  3. More visible runtime error — the current console.error fallback produces HTTP 200 with an empty <app-root>, which is indistinguishable from a working CSR app. Consider writing to stderr, returning HTTP 500, or including a clear [ACTION REQUIRED] prefix so the message is not lost in log aggregation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions