I'm on Angular v20.2 using the new SSR API to define how my routes should be rendered. I've also switched to the new AngularAppNodeEngine as seen in the docs. server.ts: import { AngularNodeAppEngine, createNodeRequestHandler, writeResponseToNodeResponse } from '@angular/ssr/node'; import express from 'express'; const app = express(); const angularApp = new AngularNodeAppEngine(); app.use('*', (req, res, next) => { angularApp .handle(req) .then(response => { if (response) { writeResponseToNodeResponse(response, res); } else { next(); // Pass control to the next middleware } }) .catch(next); }); /** * The request handler used by the Angular CLI (dev-server and during build). */ export const reqHandler = createNodeRequestHandler(app); app.routes.ts: export const routes: Route[] = [ { path: '404', loadComponent: () => import('./views/not-found/not-found.component').then(m => m.AppNotFoundComponent), }, { loadComponent: () => import('./views/page/page.component').then(m => m.AppPageComponent), matcher: url => { ... some matching logic that works fine ... }, } ]; app.routes.server.ts: import { RenderMode, ServerRoute } from '@angular/ssr'; export const serverRoutes: ServerRoute[] = [ { path: '**', renderMode: RenderMode.Server, }, { path: '404', renderMode: RenderMode.Server, status: 404, headers: { 'Cache-Control': 'no-cache', }, } ]; app.config.server.ts: import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; import { provideServerRendering, withRoutes } from '@angular/ssr'; import { appConfig } from './app.config'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [provideServerRendering(withRoutes(serverRoutes))], }; export const config = mergeApplicationConfig(appConfig, serverConfig); Then I just run this for when my server can't find a url: this.router.navigateByUrl('/404', { skipLocationChange: true }); I've also tried without skipLocationChange: true: this.router.navigateByUrl('/404'); But both give a 200 status code for the server request instead of 404. It does render the correct HTML though. What am I missing in my configuration to get the status code to work properly? Continue reading...