Dev server with SSR not accepting self-signed certificate
Which @angular/* package(s) are the source of the bug?
compiler
Is this a regression?
Yes
Description
Getting the below error, even though the self-signed certificate is trusted. Happening in SSR.
Error object: HttpErrorResponse {
headers: _HttpHeaders {
headers: Map(0) {},
normalizedNames: Map(0) {},
lazyInit: undefined,
lazyUpdate: null
},
status: 0,
statusText: 'Unknown Error',
url: 'https://localhost:7257/articles',
ok: false,
type: undefined,
redirected: undefined,
responseType: undefined,
name: 'HttpErrorResponse',
message: 'Http failure response for https://localhost:7257/articles 0 undefined',
error: TypeError: fetch failed
at node:internal/deps/undici/undici:15845:13
at process.processTicksAndRejections (node:internal/process/task_queues:103:5) {
[cause]: Error: self-signed certificate; if the root CA is installed locally, try running Node.js with --use-system-ca
at TLSSocket.onConnectSecure (node:_tls_wrap:1631:34)
at TLSSocket.emit (node:events:508:28)
at TLSSocket._finishInit (node:_tls_wrap:1077:8)
at ssl.onhandshakedone (node:_tls_wrap:863:12) {
code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
}
}
}
Only workaround I found so far is to set process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'
Setting NODE_EXTRA_CA_CERTS didn't help.
Please provide a link to a minimal reproduction of the bug
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run ng version)
Angular CLI : 21.0.0
Angular : 21.0.0
Node.js : 24.11.1
Package Manager : npm 11.6.2
Operating System : darwin arm64
┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/animations │ 21.0.0 │ ^21.0.0 │
│ @angular/build │ 21.0.0 │ ^21.0.0 │
│ @angular/cli │ 21.0.0 │ ^21.0.0 │
│ @angular/common │ 21.0.0 │ ^21.0.0 │
│ @angular/compiler │ 21.0.0 │ ^21.0.0 │
│ @angular/compiler-cli │ 21.0.0 │ ^21.0.0 │
│ @angular/core │ 21.0.0 │ ^21.0.0 │
│ @angular/forms │ 21.0.0 │ ^21.0.0 │
│ @angular/platform-browser │ 21.0.0 │ ^21.0.0 │
│ @angular/platform-server │ 21.0.0 │ ^21.0.0 │
│ @angular/router │ 21.0.0 │ ^21.0.0 │
│ @angular/ssr │ 21.0.0 │ ^21.0.0 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 5.9.3 │ ~5.9.3 │
└───────────────────────────┴───────────────────┴───────────────────┘
Anything else?
No response
Can you please try to use the latest version and see if the problem persists?
Hello @alan-agius4,
Just updated. The error is the same as below, even though NODE_EXTRA_CA_CERTS is set.
Double-checked through the console log with:
console.log('SSR NODE_EXTRA_CA_CERTS =', process.env['NODE_EXTRA_CA_CERTS']);
Error:
Error object: HttpErrorResponse {
headers: _HttpHeaders {
headers: Map(0) {},
normalizedNames: Map(0) {},
lazyInit: undefined,
lazyUpdate: null
},
status: 0,
statusText: 'Unknown Error',
url: 'https://localhost:7257/articles',
ok: false,
type: undefined,
redirected: undefined,
responseType: undefined,
name: 'HttpErrorResponse',
message: 'Http failure response for https://localhost:7257/articles: 0 undefined',
error: TypeError: fetch failed
at node:internal/deps/undici/undici:15845:13
at process.processTicksAndRejections (node:internal/process/task_queues:103:5) {
[cause]: Error: self-signed certificate; if the root CA is installed locally, try running Node.js with --use-system-ca
at TLSSocket.onConnectSecure (node:_tls_wrap:1631:34)
at TLSSocket.emit (node:events:508:28)
at TLSSocket._finishInit (node:_tls_wrap:1077:8)
at ssl.onhandshakedone (node:_tls_wrap:863:12) {
code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
}
}
}
Version:
Angular CLI : 21.0.1
Angular : 21.0.2
Node.js : 24.11.1
Package Manager : npm 11.6.2
Operating System : darwin arm64
┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/animations │ 21.0.2 │ ^21.0.2 │
│ @angular/build │ 21.0.1 │ ^21.0.1 │
│ @angular/cli │ 21.0.1 │ ^21.0.1 │
│ @angular/common │ 21.0.2 │ ^21.0.2 │
│ @angular/compiler │ 21.0.2 │ ^21.0.2 │
│ @angular/compiler-cli │ 21.0.2 │ ^21.0.2 │
│ @angular/core │ 21.0.2 │ ^21.0.2 │
│ @angular/forms │ 21.0.2 │ ^21.0.2 │
│ @angular/platform-browser │ 21.0.2 │ ^21.0.2 │
│ @angular/platform-server │ 21.0.2 │ ^21.0.2 │
│ @angular/router │ 21.0.2 │ ^21.0.2 │
│ @angular/ssr │ 21.0.1 │ ^21.0.1 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 5.9.3 │ ~5.9.3 │
└───────────────────────────┴───────────────────┴───────────────────┘
This seems like a bug but we'll need to look at a reproduction to find and fix the problem. Can you setup a minimal repro please?
You can read here why this is needed. A good way to make a minimal repro is to create a new app via ng new repro-app and adding the minimum possible code to show the problem. Then you can push this repository to github and link it here.
This might be related to your directory structure so its really important to get an accurate repro to diagnose this.
@alan-agius4, got it. Yes, I can do that. Give me some time, and I'll share the repo link here soon. I need to create a minimal repro for our .NET backend too, as it's happening when the Angular component is making an HTTP call to the backend APIs for the SSR route.
Hey @alan-agius4, here's the link for the repro:
https://github.com/mjmustafaev/angular-ssr-dotnet-selfsigned-repro
Below are the steps on how to reproduce:
Prerequisites
Make sure you have (latest stable):
- .NET SDK
- Node.js
- Angular CLI
npm install -g @angular/cli@21
1. Create a clean repro repository
mkdir angular-ssr-dotnet-selfsigned-repro
cd angular-ssr-dotnet-selfsigned-repro
git init
Create folders for backend and frontend:
mkdir backend
mkdir frontend
2. Backend – .NET HTTPS WebAPI (WeatherForecast)
2.1 Generate the WebAPI
cd backend
dotnet new webapi -n BackendApi
cd BackendApi
This creates the default WeatherForecast endpoint at /weatherforecast.
2.2 Trust the developer HTTPS certificate
Run:
dotnet dev-certs https --trust
Confirm in your OS (e.g. Keychain on macOS) that the certificate is trusted.
2.3 Enable permissive CORS
In Program.cs, add CORS and use it:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
// Add services to the container.
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseCors("AllowAll");
app.UseHttpsRedirection();
// WeatherForecast endpoint (default template)
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
2.4 Run the backend on HTTPS
Run with the HTTPS profile (the default template has one):
dotnet run --launch-profile https
Note the HTTPS URL printed in the console, e.g.:
https://localhost:7257
Check that the API works in the browser:
https://localhost:7257/weatherforecast
3. Frontend – Angular 21 SSR app
Open a new terminal tab/window, then:
cd /path/to/angular-ssr-dotnet-selfsigned-repro
cd frontend
3.1 Create Angular SSR app
Create the app with SSR enabled:
ng new ssr-client --routing --style=scss --ssr
cd ssr-client
npm install
4. Create a Weather component that calls the HTTPS API
4.1 Generate the component
ng g c weather --inline-template --inline-style
4.2 Implement the component
Edit src/app/weather/weather.ts:
import { Component, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
interface WeatherForecast {
date: string;
temperatureC: number;
summary?: string;
}
@Component({
selector: 'app-weather',
standalone: true,
imports: [],
template: `
<div class="p-4">
<h1>Weather from .NET API</h1>
@if (error()) {
<p style="color: red">Error: {{ error() }}</p>
} @else if (!data()) {
<p>Loading...</p>
} @else {
<ul>
@for (item of data(); track item.date) {
<li>
{{ item.date }} – {{ item.temperatureC }} °C – {{ item.summary || 'n/a' }}
</li>
}
</ul>
}
</div>
`
})
export class Weather {
data = signal<WeatherForecast[] | null>(null);
error = signal<string | null>(null);
constructor(http: HttpClient) {
http.get<WeatherForecast[]>('https://localhost:7257/weatherforecast')
.subscribe({
next: (result) => this.data.set(result),
error: (err) => {
console.error('HTTP error', err);
this.error.set(err?.message ?? 'Unknown error');
}
});
}
}
Important: Adjust
https://localhost:7257if your .NET HTTPS port is different.
4.3 Make WeatherComponent the root route
Edit src/app/app.routes.ts:
import { Routes } from '@angular/router';
import { Weather } from './weather/weather';
export const routes: Routes = [
{
path: '',
component: Weather,
},
];
5. Run Angular dev server over HTTPS
From frontend/ssr-client:
mkdir certs
cd certs
openssl req -x509 -newkey rsa:2048 -nodes -keyout angular-dev.key -out angular-dev.crt -days 365 -subj "/CN=localhost"
cd ..
Run Angular dev server with HTTPS:
ng serve --ssl true --ssl-cert ../certs/angular-dev.crt --ssl-key ../certs/angular-dev.key
Angular now serves:
https://localhost:4200/
The SSR error will reproduce when Angular calls the HTTPS backend.
6. Trigger the issue
With:
- .NET backend running (
dotnet run --launch-profile https) - Angular dev server running (
ng serve ...)
Open in the browser:
https://localhost:4200/
On first load, Angular SSR attempts to execute:
http.get('https://localhost:7257/weatherforecast')
6.1 Browser behaviour
- Directly opening
https://localhost:7257/weatherforecastin the browser works. - After hydration, client-side HTTP calls from Angular succeed (certificate trusted).
6.2 SSR / Node behaviour
In the Angular dev server console, you see errors similar to:
HTTP error HttpErrorResponse {
status: 0,
statusText: 'Unknown Error',
url: 'https://localhost:7257/weatherforecast',
message: 'Http failure response for https://localhost:7257/weatherforecast: 0 undefined',
error: TypeError: fetch failed
at node:internal/deps/undici/undici:...
...
[cause]: Error: self-signed certificate; if the root CA is installed locally,
try running Node.js with --use-system-ca {
code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
}
}
This shows:
- Browser trusts and calls the API fine.
-
SSR (Node + undici/fetch) rejects the same certificate with
DEPTH_ZERO_SELF_SIGNED_CERT.