This website uses cookies

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance.

Sep 15, 2026 23:59:59

Limited-Time

Summer Offer

  • 0

    Days

  • 0

    Hours

  • 0

    Min

  • 0

    Sec

40% OFF

All Hosting Plans +
Unlimited Free Migrations

CLAIM NOW

*Valid till 15th September 2026

Angular Server Side Rendering: Complete Build and Deployment Guide

Updated on August 5, 2026

11 Min Read
Diagram of an Angular server sending fully rendered HTML to the browser before hydration

Key Takeaways

  • Angular Server-Side Rendering builds the page on a Node.js server and sends fully rendered HTML to the browser, using non-destructive hydration and event replay to avoid UI flicker.
  • Serverless platforms like Vercel and AWS Lambda cause cold starts, adding 2 to 3 seconds to Time-to-First-Byte and erasing the speed benefits SSR is supposed to deliver.
  • This guide builds a data-driven Angular SSR app that fetches data from an external API at render time and serializes it directly into the HTML.
  • The finished app deploys to Cloudways Velocity, a persistent managed Node.js environment with zero cold starts and automated GitHub deployments.

By default, an Angular app runs in the browser. The server hands over a mostly empty tag, and then the user just waits while the JavaScript bundles download and run before anything shows up on the page.

On a slow connection, that’s a blank screen for a second or two. And crawlers that don’t handle JavaScript well end up seeing hardly any of your content.

Angular Server Side Rendering fixes this by running that first render on a Node.js server instead. The server builds the page, grabs the data it needs, and sends back fully rendered HTML right away. So the user sees real content almost instantly, and crawlers get a full page to read.

The thing is, how you deploy this matters just as much as enabling it. Get it wrong and you either lose the speed you were after, or the rendered HTML never reaches the browser the way you intended.

In this guide, I’ll go over why Angular SSR is worth using and how it works. Then I’ll build a data-driven Angular app with SSR enabled, and deploy its prerendered output to a managed Node.js environment (Cloudways Velocity) so users and crawlers get fully populated HTML on the very first load.

Why Use Angular Server-Side Rendering?

Client-Side Rendering (CSR) hands all the work to the user’s browser. The server gives it a skeletal HTML file, and until your JavaScript bundles run and your API calls come back, the user just stares at a blank screen.

SSR flips that around and shifts the work back to the server.

By that I mean a request hits your Node.js backend, Angular runs through its lifecycle and fetches your API data, builds the component tree, and then the server turns that DOM into raw HTML and sends it straight back.

The browser paints the page instantly. But client-side Angular still needs to step in to make everything interactive, and modern Angular handles that handoff with two features worth knowing about.

The first one is non-destructive hydration. Instead of wiping out the server-rendered HTML and re-rendering the whole thing from scratch (which is what causes that annoying UI flicker), Angular scans the DOM that’s already there and just attaches its event listeners to it.

The second is event replay. Say a user clicks a button before the client-side JavaScript has finished loading. Angular holds onto that click and replays it the second hydration finishes, so nothing gets lost.

But all of this hangs on one thing: a fast server response. If your Node process is stuck waking up from a serverless cold start, none of these optimizations matter, because the page load is already ruined before they get a chance to kick in.

The Cold Start Problem in Serverless SSR

If you look at platforms like Vercel, Netlify, or AWS Amplify, they push serverless architecture pretty hard. They want you deploying your Angular SSR app as an AWS Lambda function (or an Edge function).

And the developer experience really is great. You connect your GitHub repo, push to main, and they handle the build for you. But for SSR specifically, the architecture is fundamentally flawed.

Here’s why.

Serverless functions are ephemeral. To save money and compute, cloud providers don’t keep your Node.js server running 24/7. So if your app goes 10 or 15 minutes without a visitor, the provider shuts the container down. It goes to sleep.

Then the next user clicks a link to your site, and now the provider has to spin up a new micro-container, load the Node.js runtime, fetch your Angular server.mjs bundle, execute the Express server, and only then run your Angular SSR logic to actually generate the HTML.

That whole wake-up is called a cold start, and it usually adds 2 to 3 seconds to your Time-to-First-Byte (TTFB).

Think about that for a second. If you spent days getting your Angular app down to a 200ms TTFB, then wrapped it in a serverless function that falls asleep and tacks on a 3000ms delay, you’ve basically thrown away the whole performance advantage of SSR.

So for the speed benefits to actually land, your Node server needs to be persistent. By that I mean it stays awake 24/7, ready to handle requests instantly, without booting up a fresh environment every time.

Comparing Angular SSR Hosting Options

So if serverless creates cold starts, where should you actually put your compiled Angular app? There are really three tiers to weigh up for Node.js apps.

Hosting Type Environment State TTFB Impact DevOps Required Example Providers
Serverless Ephemeral High, 2-3s cold starts Low, automated Vercel, AWS Lambda
Raw VPS Persistent Instant High, manual Nginx/PM2 DigitalOcean, EC2
Managed Node.js Persistent Instant Low, automated Cloudways Velocity

First up is serverless. Like I said, it’s easy to deploy but you pay for it with cold starts. It’s great for static client-side apps, but pretty terrible for compute-heavy SSR.

Then there’s the raw VPS route. You rent something like a $5 Ubuntu droplet on DigitalOcean, your Node server stays on 24/7, and there are zero cold starts.

The catch? You’re now a sysadmin. You’ve got to SSH into the server yourself, install Node, set up an Nginx reverse proxy to expose port 4000 to port 80, configure PM2 so the app comes back if it crashes, and renew your Let’s Encrypt SSL certificates by hand.

And then there’s the managed route, which is what I actually use for production Angular SSR. Platforms like Cloudways Velocity hand you a persistent container, so your Node app stays awake 24/7 with zero cold starts, except now the platform deals with the Nginx proxy, the SSL, and the automated GitHub deployments for you.

So you get the raw speed of a VPS without any of the sysadmin work.

In the next section, I’ll scaffold a new Angular app with SSR enabled and build a component that fetches data on the server, so we can prove this setup works in production.

Get VPS Speed Without the Sysadmin Work

Cloudways Velocity gives your Angular SSR app a persistent Node.js container with automated GitHub deployments.

Building a Data-Driven Angular SSR App

A static “Hello World” app won’t cut it here. I need something that actually does server-side work, so I can prove the hosting setup holds up.

So I’ll build a simple standalone Angular app that calls an external API (JSONPlaceholder) and pulls a list of posts. The goal is simple: watch Angular fetch that data during render and get it into the HTML before it ever reaches the browser.

Step 1: Setting Up the Angular Environment

Before I scaffold anything, I need Node in my terminal. Since I’m on my work laptop with IT restrictions, I can’t run a normal installer, so I’m using the standalone Node.js binary instead. That means CMD doesn’t know where my Node tools are until I tell it. To sort that out, I’ll open CMD and point it at the folder where I unzipped Node:

PATH=%PATH%;C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64

With that set, I’ll use the Angular CLI to scaffold a new workspace.

npx @angular/cli new angular-ssr-demo --ssr

During the setup prompts, I just press Enter to stick with standard CSS. I also skipped on integrating AI tools to keep my workspace clean.

Angular CLI setup prompts for scaffolding a new SSR workspace in the terminal

Once the install finishes, I’ll go in my directory:

cd angular-ssr-demo

Using the –ssr flag tells Angular to generate a server.ts file, which is your Express backend, and to automatically set up the server build targets inside angular.json.

The more important thing it does is configure modern hydration. If I open src/app/app.config.ts, I want to keep the defaults the CLI generated (like provideBrowserGlobalErrorListeners), but I need to make sure both routing and hydration are enabled, plus HttpClient so the server itself can make API calls.

Default app.config.ts file generated by the Angular CLI with the --ssr flag

I’ll also add withEventReplay(), which makes sure any clicks a user fires off before the JavaScript finishes loading get captured rather than lost.

// src/app/app.config.ts
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes),
    // Enables non-destructive hydration and buffers early user clicks
    provideClientHydration(withEventReplay()),
    // withFetch() is required for SSR to make HTTP calls natively in Node
    provideHttpClient(withFetch())
  ]
};

Updated app.config.ts file with client hydration, event replay, and HttpClient providers

Step 2: Building the Data-Fetching Component

Now I’ll build the component that actually fetches the posts and renders them. This is the piece that proves the point: the server runs this fetch and the result ends up in the HTML before the browser ever sees it.

I’ll open src/app/app.ts and replace what’s there with my own component. It defines the shape of a post, calls the JSONPlaceholder API inside ngOnInit, and then loops over the results in the template to display each one.

// src/app/app.ts
import { Component, inject, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';

interface Post {
  id: number;
  title: string;
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule],
  template: `
    <main style="font-family: sans-serif; padding: 2rem;">
      <h1>Angular SSR Data Fetching</h1>
      <p>If you View Page Source, this list is baked into the raw HTML:</p>
      <ul>
        <li *ngFor="let post of posts">
          <strong>{{ post.id }}</strong> - {{ post.title }}
        </li>
      </ul>
    </main>
  `
})
export class App implements OnInit {
  private http = inject(HttpClient);
  posts: Post[] = [];

  ngOnInit() {
    // The server waits for this request to finish before rendering the HTML
    this.http.get<Post[]>('https://jsonplaceholder.typicode.com/posts?_limit=5')
      .subscribe(data => {
        this.posts = data;
      });
  }
}

app.ts file with the Post interface and JSONPlaceholder API call in VS Code

Because I set up HttpClient with withFetch() back in Step 1, the server can run this HTTP call natively during the render. So by the time the page reaches the browser, the list of posts is already sitting in the HTML.

Step 3: Test the App Locally

To confirm the server is actually doing the rendering work, I’ll spin up the local development server.

npm run start

Once the server runs, in my command prompt window, I should see “Application bundle generation complete.

Terminal output showing Application bundle generation complete after npm run start

With that, I’ll open http://localhost:4200 in my browser.

Angular SSR demo app running in the browser at localhost:4200 showing the post list

The way the page looks isn’t really the test I care about. What I actually want to check is what the server sent down in the first place.

So I’ll copy the first listed item on the page, then view the page’s source.

Viewing the page source of the Angular SSR app in the browser

If I CTRL+F what I copied, instead of an empty shell waiting on JavaScript, I can see my post data already embedded in the raw HTML the server sent. Angular fetched the data while building the page and serialized it straight into the response.

Post data found in the raw HTML source confirming server-side rendering worked

That’s the whole point of rendering on the server side: the content is there on first load, before any client-side JavaScript runs.

Now that the app works, the next job is to build it for production and get it live.

Step 4: Building the Application for Production

Since the data-fetching works locally, the next step is compiling the app for production.

npm run build

Terminal output from running npm run build for the Angular SSR app

A regular client-side Angular build just drops a pile of static files into a single folder, but the SSR build works differently and produces a specific structure inside dist/angular-ssr-demo/.

When I open that dist directory, there are two main folders. The browser/ folder holds the standard client-side assets, the CSS, the images, and the JavaScript bundles that take over once hydration kicks in. The server/ folder holds the compiled Node.js Express server that does the actual rendering.

The part I care about for deployment is the browser/ folder. During the build, Angular prerenders my route and drops the fetched data straight into the HTML inside that folder. So when I deploy, that’s the folder I’ll point Cloudways at, since it already holds fully rendered pages ready to serve.

Step 5: Deploying Angular SSR to Cloudways Velocity

Now I’ll get this live on Cloudways Velocity, a managed Node.js environment that handles the Nginx proxy, SSL, and automated GitHub deployments for me.

First, I’ll commit the whole project and push it to a new GitHub repository. Git already ignores node_modules and the dist folder by default, so those stay out of it.

Terminal commands committing and pushing the Angular SSR project to GitHub

New GitHub repository created for the Angular SSR project

GitHub repository showing the pushed Angular SSR project files

Once that’s pushed, I’ll log into Cloudways and set up the deployment.

I’ll start by launching a Velocity app from the Cloudways dashboard, and pick a server size.

Cloudways dashboard for launching a new Velocity app and choosing a server size

Cloudways Velocity app creation screen with server size options

Next, I’ll connect my GitHub account, and select the angular-server-side-rendering repository I just pushed.

Connecting a GitHub account in Cloudways and selecting the project repository

Cloudways screen after selecting the angular-server-side-rendering GitHub repository

Now Cloudways automatically figures out the settings for my app. It sets the framework to Angular, the branch to main, and the Node version to v24. I don’t have to configure any of these settings manually.

Cloudways auto-detected settings showing Angular framework, main branch, and Node v24

There’s one setting I do need to point at the right place, and that’s the Output Directory. Angular’s build produces its prerendered, browser-ready files inside a browser subfolder, so I’ll set the Output Directory to:

dist/angular-ssr-demo/browser

Setting the Output Directory to dist/angular-ssr-demo/browser in Cloudways

That’s the folder holding the prerendered HTML with my data already in it, along with the client-side bundles that hydrate the page once it loads.

Once I hit deploy, Cloudways pulls the repo, runs npm install, runs the build, and serves that prerendered output.

Cloudways deployment log pulling the repository and installing dependencies

Cloudways deployment log completing the build and starting the app

When it finishes, the app is live on the temporary Cloudways URL. I open View Page Source and there’s my post data, right in the raw HTML, exactly like it was locally.

Live Angular SSR app page source on Cloudways showing the post data in raw HTML

Live Angular SSR demo app running on the Cloudways temporary URL

Conclusion

Angular SSR isn’t just a checkbox for a better Lighthouse score. It changes how your app actually runs, from a static build into a live server-side process, and that’s worth keeping in mind when you decide where to host it.

The payoff is simple: whoever loads the page, or whatever crawler indexes it, gets real content right away instead of an empty shell waiting on JavaScript.

And if you deploy that prerendered output to a managed host like Cloudways Velocity, you get all of that without touching Nginx or wrangling servers yourself.

Deploy Your Angular SSR App on Cloudways

Connect your repo, set the output directory, and get persistent Node.js hosting with zero cold starts.

Q. Does Angular SSR trigger duplicate HTTP requests on the client?

It doesn’t, and that’s actually one of the nicer parts of how it works. Angular serializes the data it fetched on the server directly into the HTML payload, so when the client-side HttpClient comes along it just reuses that existing data instead of firing off the same network request a second time.

Q. What’s the main difference between Angular SSR and SSG (Prerendering)?

The difference comes down to when the HTML gets built. SSR generates fresh HTML on every server request, which is exactly what you want when the data is live and changing. SSG builds all its static HTML once ahead of time during npm run build, which makes it the better fit for content that doesn’t really change between deploys.

Q. Will SSR fix all of my app’s performance issues?

Not entirely, and it’s worth being upfront about where its limits are. SSR improves the initial page load, so your TTFB and FCP get better, and it helps with SEO indexing too. But once hydration has finished, it won’t do a thing for poorly written client-side code or heavy, unoptimized assets, and those remain something you have to handle yourself.

Share your opinion in the comment section. COMMENT NOW

Share This Article

Start Growing with Cloudways Today.

Our Clients Love us because we never compromise on these

Abdul Rehman

Abdul is a tech-savvy, coffee-fueled, and creatively driven marketer who loves keeping up with the latest software updates and tech gadgets. He's also a skilled technical writer who can explain complex concepts simply for a broad audience. Abdul enjoys sharing his knowledge of the Cloud industry through user manuals, documentation, and blog posts.

×

Webinar: How to Get 100% Scores on Core Web Vitals

Join Joe Williams & Aleksandar Savkovic on 29th of March, 2021.

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Want to Experience the Cloudways Platform in Its Full Glory?

Take a FREE guided tour of Cloudways and see for yourself how easily you can manage your server & apps on the leading cloud-hosting platform.

Start my tour