Skip to content

Prerendered HTML files not served in Angular 21 SSR #32751

@apappas1129

Description

@apappas1129

Description

When creating a new Angular 21 project with SSR and prerendering enabled, the returned HTML from the server does not include the prerendered content. Instead, it returns the client-side rendered HTML (index.csr.html). The fully prerendered index.html is not being served.

Steps to reproduce

  1. Run ng new scaffolding-ng21 with SSR and prerendering enabled.
  2. Build the project:
    ng build
    
  3. Serve SSR:
    npm run serve:ssr:scaffolding-ng21
    
  4. Open http://localhost:4000 in a browser and inspect the network response.

Expected behavior
The returned HTML should be the prerendered index.html generated by Angular, containing the fully rendered content of the AppComponent from the scaffolding template.

Actual behavior
The server returns the client-side rendered HTML (index.csr.html) instead of the prerendered HTML, resulting in an empty shell and missing prerendered content.

Additional context

app.routes.server.ts:

import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  {
    path: '**',
    renderMode: RenderMode.Prerender
  }
];

package.json:

{
  "name": "scaffolding-ng21",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "serve:ssr:scaffolding-ng21": "node dist/scaffolding-ng21/server/server.mjs"
  },
  "private": true,
  "packageManager": "npm@10.9.2",
  "dependencies": {
    "@angular/common": "^21.2.0",
    "@angular/compiler": "^21.2.0",
    "@angular/core": "^21.2.0",
    "@angular/forms": "^21.2.0",
    "@angular/platform-browser": "^21.2.0",
    "@angular/platform-server": "^21.2.0",
    "@angular/router": "^21.2.0",
    "@angular/ssr": "^21.2.1",
    "express": "^5.1.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0"
  },
  "devDependencies": {
    "@angular/build": "^21.2.1",
    "@angular/cli": "^21.2.1",
    "@angular/compiler-cli": "^21.2.0",
    "@types/express": "^5.0.1",
    "@types/node": "^20.17.19",
    "jsdom": "^28.0.0",
    "prettier": "^3.8.1",
    "typescript": "~5.9.2",
    "vitest": "^4.0.8"
  }
}

default/scaffolding server.ts:

import {
  AngularNodeAppEngine,
  createNodeRequestHandler,
  isMainModule,
  writeResponseToNodeResponse,
} from '@angular/ssr/node';
import express from 'express';
import { join } from 'node:path';

const browserDistFolder = join(import.meta.dirname, '../browser');

const app = express();
const angularApp = new AngularNodeAppEngine();

/**
 * Example Express Rest API endpoints can be defined here.
 * Uncomment and define endpoints as necessary.
 *
 * Example:
 * ```ts
 * app.get('/api/{*splat}', (req, res) => {
 *   // Handle API request
 * });
 * ```
 */

/**
 * Serve static files from /browser
 */
app.use(
  express.static(browserDistFolder, {
    maxAge: '1y',
    index: false,
    redirect: false,
  }),
);

/**
 * Handle all other requests by rendering the Angular application.
 */
app.use((req, res, next) => {
  angularApp
    .handle(req)
    .then((response) =>
      response ? writeResponseToNodeResponse(response, res) : next(),
    )
    .catch(next);
});

/**
 * Start the server if this module is the main entry point, or it is ran via PM2.
 * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
 */
if (isMainModule(import.meta.url) || process.env['pm_id']) {
  const port = process.env['PORT'] || 4000;
  app.listen(port, (error) => {
    if (error) {
      throw error;
    }

    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

/**
 * Request handler used by the Angular CLI (for dev-server and during build) or Firebase Cloud Functions.
 */
export const reqHandler = createNodeRequestHandler(app);

Proof that index.html was successfully pre-rendered:

Image

Proof that server responded with the contents of index.csr.html instead of the pre-rendered index.html that should contain the complete home page:

Image Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions