Deploy a Server-Side Rendered Angular 20+… | LaunchFast
LaunchFast LogoLaunchFast
Blog
771 words4 min read

Deploy a Server-Side Rendered Angular 20+ Application to Cloudflare Workers

Learn how to deploy a server-side rendered Angular 20+ application to Cloudflare Workers, serving static assets from the edge and running SSR.

Rishi Raj Jain
Rishi Raj JainAuthor
Deploy a Server-Side Rendered Angular 20+ Application to Cloudflare Workers

In this guide, I will walk you through the steps to deploy a server-side rendered Angular 20+ application on Cloudflare Workers, a powerful serverless platform that allows you to run your Angular app on Cloudflare’s global edge network.

Prerequisites

You’ll need the following:

Demo Application

You can see a working example of this deployment in action at https://angular-ssr-app.launchfast.workers.dev/posts. This demonstrates a fully server-side rendered Angular application running on Cloudflare Workers with zero cold starts.

Understanding the Architecture

When deploying Angular SSR to Cloudflare Workers, the application is split into two parts:

  1. Browser Build (dist/<app>/browser/*) - Static assets served directly by Cloudflare’s asset handling, bypassing the Worker
  2. Server Build (dist/<app>/server/*) - Bundled into the Worker for server-side rendering

The Cloudflare Worker Bridge

Create an entry point for your Cloudflare deployment lives at cloudflare/worker.ts. This file bridges your Angular SSR application to the Cloudflare Workers runtime. Here’s what it does:

cloudflare/worker.ts
import {
AngularAppEngine,
createRequestHandler,
ɵsetAngularAppEngineManifest as setAngularAppEngineManifest,
} from '@angular/ssr';
// Generated build artifact containing the engine manifest
import engineManifest from '../dist/angular-ssr-app/server/angular-app-engine-manifest.mjs';
setAngularAppEngineManifest(engineManifest);
const angularApp = new AngularAppEngine();
/**
* createRequestHandler returns a Web-standard (Request) => Response handler,
* which is exactly the shape Cloudflare's fetch handler expects.
*/
const fetch = createRequestHandler(async (request: Request) => {
const response = await angularApp.handle(request);
return response ?? new Response('Not found', { status: 404 });
});
export default { fetch };

Key Points

  • AngularAppEngine is initialized to handle SSR rendering
  • The engine manifest is imported from your build output: it contains inlined HTML templates and build metadata
  • createRequestHandler wraps Angular’s engine in a Web-standard fetch handler that Cloudflare expects
  • Any request that doesn’t match a route returns a 404

Update Wrangler Configuration

Update your wrangler.jsonc file to the following:

wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "angular-ssr-app",
"main": "cloudflare/worker.ts",
"compatibility_date": "2025-05-05",
// Enable Node.js compatibility for Angular's SSR dependencies
"compatibility_flags": ["nodejs_compat"],
// Define import.meta.url for polyfills compatibility
"define": {
"import.meta.url": "\"file:///worker.mjs\""
},
// Serve browser build as static assets
"assets": {
"directory": "./dist/angular-ssr-app/browser",
"binding": "ASSETS"
},
}

Configuration Explained

  • compatibility_flags: ["nodejs_compat"] - Required because Angular’s server polyfills use Node.js built-ins
  • define - Provides a fallback for import.meta.url which gets collapsed during bundling
  • assets.directory - Points to your browser build for static file serving

Build Configuration for Cloudflare

Your angular.json configures how Angular builds for the Cloudflare platform:

{
"projects": {
"angular-ssr-app": {
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"browser": "src/main.ts",
"server": "src/main.server.ts",
"outputMode": "server",
"ssr": {
"entry": "src/server.ts"
},
"security": {
"allowedHosts": [
"localhost",
"angular-ssr-app.launchfast.workers.dev"
]
}
}
}
}
}
}
}

SSRF Protection with Allowed Hosts

The allowedHosts setting prevents Server-Side Request Forgery (SSRF) attacks:

  • Only the hostnames listed here can be server-side rendered
  • Add your production domain and preview domains
  • At deploy time, you can add additional hosts via the NG_ALLOWED_HOSTS environment variable without rebuilding

Building and Deploying

Your package.json includes convenient scripts for testing and deploying:

{
"scripts": {
"build": "ng build",
"preview:cf": "npm run build && wrangler dev",
"deploy:cf": "npm run build && wrangler deploy"
}
}

Local Testing

To test your application locally before deploying:

Terminal window
npm run preview:cf

This builds your Angular application and starts the Wrangler dev server on http://localhost:8787.

Deployment to Cloudflare

When ready to deploy to production:

Terminal window
npm run deploy:cf

This:

  1. Builds your Angular SSR application for production
  2. Bundles the server code into the Worker using esbuild
  3. Uploads your browser assets to Cloudflare
  4. Deploys your Worker globally across Cloudflare’s network

Environment Variables and Allowed Hosts

For staging or preview deployments with different domains, you can specify additional allowed hosts at deploy time without rebuilding:

Terminal window
NG_ALLOWED_HOSTS=staging.example.com,preview.example.com npm run deploy:cf

The environment variable accepts comma-separated hostnames and is merged with the hosts defined in angular.json at runtime.

Conclusion

Yay! You now have an Angular 20+ SSR project that automatically deploys to Cloudflare without any additional configuration within the Angular scope.

If you have questions or encounter issues, refer to the Angular SSR documentation and Cloudflare Workers documentation.

Continue reading