Cloud & Déploiement angularforall.com

- Angular SSR : Universal sur Vercel en production

Angular Ssr Vercel Angular-Universal Edge-Runtime Prerender Hydration Seo Lighthouse Deploiement Serverless Cloud Core-Web-Vitals Isr
Angular SSR : Universal sur Vercel en production

Déployez Angular Universal SSR sur Vercel : prérendu hybride, vercel.json, edge runtime, hydration et SEO optimisé pour applications Angular.

Pourquoi SSR pour une application Angular ?

Une application Angular classique (CSR — Client-Side Rendering) sert un index.html quasi vide. Le navigateur télécharge ensuite le bundle JavaScript, démarre Angular, exécute les composants et seulement à ce moment le contenu apparaît. Sur connexion mobile 4G médiane, ce processus prend 3 à 5 secondes : le robot Googlebot voit une page blanche, les balises Open Graph sont absentes lors du partage social, et le Largest Contentful Paint (LCP) devient critique pour le SEO.

Le Server-Side Rendering avec Angular Universal résout ce problème en exécutant le rendu sur Node.js (ou un edge runtime) avant d'envoyer le HTML complet au navigateur. Le client reçoit immédiatement un document indexable, puis Angular « hydrate » l'application — c'est-à-dire qu'il associe les composants TypeScript aux nœuds DOM existants sans recharger.

Bénéfices mesurables en production

  • SEO : indexation immédiate, balises meta dynamiques visibles, rich snippets opérationnels
  • Performance perçue : First Contentful Paint divisé par 3 ou 4 sur mobile
  • Partage social : Open Graph et Twitter Cards générés côté serveur
  • Accessibilité : contenu disponible sans JavaScript (lecteurs d'écran low-end, navigateurs limités)
  • Conversion : 100ms de gain sur le LCP = environ 1% de conversion supplémentaire d'après les études Akamai et Google

Pourquoi déployer sur Vercel précisément ?

Vercel est conçu pour les frameworks SSR modernes. Sa plateforme combine un CDN edge multi-régions, des Serverless Functions Node.js qui exécutent le rendu Angular, un edge runtime V8 isolates pour des cold starts millisecondes, du Preview Deployment automatique sur chaque pull request GitHub, et un build cache distribué qui accélère drastiquement les déploiements répétés.

Coût en production : Le plan Hobby de Vercel est gratuit (100 GB-h de compute, 100 GB de bandwidth, builds illimités). Pour un trafic professionnel, le plan Pro à 20$/utilisateur/mois inclut 1 TB de bandwidth, 1 000 GB-h de compute, analytics web vitals et observability. Comparé à un déploiement EC2 + ALB + CloudFront équivalent (~80$/mois), Vercel reste très compétitif.

Activer SSR avec ng add @angular/ssr

Depuis Angular 17, le paquet officiel @angular/ssr remplace l'ancien @nguniversal/express-engine. Une seule commande suffit pour ajouter SSR à un projet existant : tout est généré, aucune réécriture des composants n'est nécessaire.

Prérequis

# Vérifier la version Node (Vercel exige Node 20+ en 2026)
node --version
# v20.18.0 ou supérieur

# Vérifier Angular CLI
ng version
# Angular CLI: 19.x ou 20.x recommandé

# Mise à jour si besoin
npm install -g @angular/cli@latest

Installation du paquet SSR

# Dans la racine du projet Angular existant
ng add @angular/ssr

# La CLI demande :
# ? Would you like to enable Server-Side Rendering (SSR)
#   and Static Site Generation (SSG/Prerendering)? Yes

Fichiers générés automatiquement

La commande modifie ou crée plusieurs fichiers : src/app/app.config.server.ts (configuration spécifique serveur), src/main.server.ts (point d'entrée bootstrap serveur), src/server.ts (serveur Express qui sert les requêtes), et met à jour angular.json avec une nouvelle configuration server et un block prerender.

// src/app/app.config.server.ts
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { provideServerRouting } from '@angular/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';

const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering(),
    // Active le routage spécifique au serveur (prerender, SSR, ISR)
    provideServerRouting(serverRoutes),
  ],
};

// Merge avec la config client commune (router, http, etc.)
export const config = mergeApplicationConfig(appConfig, serverConfig);
// src/app/app.routes.server.ts
import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  // Page d'accueil : prerender au build (SSG pur)
  { path: '', renderMode: RenderMode.Prerender },

  // Pages statiques : prerender
  { path: 'about', renderMode: RenderMode.Prerender },
  { path: 'pricing', renderMode: RenderMode.Prerender },

  // Articles dynamiques : SSR à chaque requête
  { path: 'blog/:slug', renderMode: RenderMode.Server },

  // Dashboard utilisateur : pas de SSR (CSR pur, derrière auth)
  { path: 'dashboard/**', renderMode: RenderMode.Client },

  // Catch-all : SSR par défaut
  { path: '**', renderMode: RenderMode.Server },
];

Build local — vérifier le SSR

# Build production avec SSR
npm run build

# Démarrer le serveur Node local pour tester
npm run serve:ssr:my-app

# Ouvrir http://localhost:4000 et inspecter la source HTML
# Le contenu doit être présent dans la première réponse,
# pas seulement chargé après JavaScript
curl -s http://localhost:4000 | grep -i "<h1"
# <h1>Bienvenue sur mon site Angular</h1>  ← présent immédiatement
Si curl ne renvoie pas le HTML rendu, vérifiez que vous avez lancé serve:ssr et non start. La commande ng serve ne fait jamais de SSR — c'est le mode dev pur navigateur.

Configuration vercel.json pour Angular

Vercel détecte automatiquement Angular grâce à angular.json, mais pour SSR il est indispensable de fournir un fichier vercel.json à la racine. Ce fichier déclare la build, les fonctions serverless et le routage des requêtes vers le bundle SSR.

vercel.json minimal pour Angular SSR

{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "version": 2,
  "buildCommand": "npm run build",
  "outputDirectory": "dist/my-app",
  "installCommand": "npm ci",
  "framework": null,
  "functions": {
    "api/server.js": {
      "runtime": "nodejs20.x",
      "memory": 1024,
      "maxDuration": 30
    }
  },
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/api/server"
    }
  ]
}

Adapter Vercel — fichier api/server.js

// api/server.js — handler Vercel qui appelle le serveur Angular SSR
// Ce fichier doit être à la racine du projet, dans /api
import { createNodeRequestHandler } from '@angular/ssr/node';
import bootstrap from '../dist/my-app/server/main.server.mjs';

// Vercel exige un default export compatible Node HTTP
export default createNodeRequestHandler(bootstrap);

angular.json — vérifier les outputs

{
  "projects": {
    "my-app": {
      "architect": {
        "build": {
          "builder": "@angular/build:application",
          "options": {
            "outputPath": "dist/my-app",
            "browser": "src/main.ts",
            "server": "src/main.server.ts",
            "ssr": {
              "entry": "src/server.ts"
            },
            "prerender": {
              "discoverRoutes": true,
              "routesFile": "src/prerender-routes.txt"
            }
          }
        }
      }
    }
  }
}

Premier déploiement

# Installer la CLI Vercel
npm install -g vercel

# Lier le projet (création d'un projet Vercel)
vercel link

# Déployer en preview (URL temporaire)
vercel

# Déployer en production
vercel --prod

# Sortie attendue
# Inspect: https://vercel.com/account/my-app/abc123  [2s]
# Production: https://my-app.vercel.app  [42s]
Build cache Vercel : Le second déploiement est typiquement 60% plus rapide que le premier. Vercel met en cache node_modules, le cache Angular CLI (.angular/cache) et les artefacts intermédiaires. Pour invalider manuellement, utilisez vercel --force ou supprimez le cache dans Settings → General → Build Cache.

Routes prerender et ISR hybride

Le prerender (Static Site Generation) génère le HTML au build. C'est l'option la plus rapide à servir : le CDN Vercel renvoie un fichier statique sans cold start. Pour les routes dynamiques (articles, fiches produit), Angular 19+ supporte l'Incremental Static Regeneration (ISR) via le paramètre RenderMode.Prerender combiné à des route parameters.

Prerender de routes dynamiques avec getPrerenderParams

// src/app/app.routes.server.ts
import { RenderMode, ServerRoute } from '@angular/ssr';
import { inject } from '@angular/core';
import { ArticleService } from './services/article.service';

export const serverRoutes: ServerRoute[] = [
  {
    path: 'blog/:slug',
    renderMode: RenderMode.Prerender,
    // Liste les slugs à prerender au build
    async getPrerenderParams() {
      const articleService = inject(ArticleService);
      const articles = await articleService.fetchAllSlugs();
      // Retourne un tableau d'objets { slug: '...' }
      return articles.map(a => ({ slug: a.slug }));
    },
  },
  // Fallback : si un nouveau slug arrive après le build → SSR à la demande
  { path: 'blog/**', renderMode: RenderMode.Server },
];

Cache hybride avec ISR — Vercel revalidate

// api/server.js — ajouter des headers de cache CDN
import { createNodeRequestHandler } from '@angular/ssr/node';
import bootstrap from '../dist/my-app/server/main.server.mjs';

const baseHandler = createNodeRequestHandler(bootstrap);

export default async function handler(req, res) {
  // Cache CDN Vercel : 1h fresh, 24h stale-while-revalidate
  // → contenu servi instantanément depuis l'edge
  // → régénéré en arrière-plan si plus vieux que 1h
  res.setHeader(
    'Cache-Control',
    's-maxage=3600, stale-while-revalidate=86400'
  );

  return baseHandler(req, res);
}

Comportement résultant

Type de route Mode Angular Latence typique Cas d'usage
/, /about RenderMode.Prerender 20–40 ms (CDN) Pages marketing statiques
/blog/:slug (connus) Prerender + ISR 30 ms (cache hit) Articles, fiches produit catalogue
/search?q=... RenderMode.Server 200–400 ms (cold) Résultats dynamiques personnalisés
/dashboard RenderMode.Client n/a (CSR pur) App authentifiée, données privées

Forcer un rebuild Vercel après publication

# Webhook Deploy Hook : créer une URL POST dans Settings → Git
# Puis depuis le CMS / backend après publication d'un article :

curl -X POST \
  https://api.vercel.com/v1/integrations/deploy/prj_abc/abc123hook

# → Vercel lance un build complet
# → Le nouveau slug devient prerendered en ~2 min
Pour une publication temps réel sans rebuild complet, gardez la route en RenderMode.Server avec stale-while-revalidate. Le compromis : le cold start de la première visite (200–400ms) versus la fraîcheur immédiate du contenu.

Edge Runtime pour Angular SSR

L'Edge Runtime de Vercel exécute votre code dans des V8 isolates répartis sur 30+ régions mondiales. Comparé à une Serverless Function Node.js classique, l'edge offre des cold starts en dizaines de millisecondes (vs 300–600ms pour Node) et une latence réduite grâce à la proximité géographique avec l'utilisateur.

Limites du runtime Edge pour Angular

Le runtime Edge est basé sur une API Web standard (Fetch, Request, Response, Streams) et n'expose pas l'intégralité de Node.js. Avant d'activer Edge sur une route Angular SSR, vérifiez que votre code respecte ces limites :

  • Pas de modules Node natifs (fs, net, child_process)
  • Pas de dépendances qui les utilisent en transitif (vérifier avec npm ls fs)
  • Taille du bundle Edge limitée à 4 MB après compression
  • Durée d'exécution limitée à 25 secondes (vs 30s en Node serverless)
  • Pas de connexion TCP brute (utiliser fetch uniquement)

Configurer une route Edge dans vercel.json

{
  "version": 2,
  "buildCommand": "npm run build",
  "outputDirectory": "dist/my-app",
  "functions": {
    "api/server-node.js": {
      "runtime": "nodejs20.x",
      "memory": 1024,
      "maxDuration": 30
    },
    "api/server-edge.js": {
      "runtime": "edge",
      "memory": 128
    }
  },
  "rewrites": [
    { "source": "/blog/(.*)", "destination": "/api/server-edge" },
    { "source": "/(.*)",      "destination": "/api/server-node" }
  ]
}

Handler Edge Angular

// api/server-edge.js — handler edge runtime
// ATTENTION : utiliser uniquement les API Web (pas Node)
export const config = {
  runtime: 'edge',
  regions: ['cdg1', 'iad1', 'sfo1'], // Paris, Virginie, San Francisco
};

import { renderApplication } from '@angular/platform-server';
import bootstrap from '../dist/my-app/server/main.server.mjs';

export default async function handler(request) {
  const url = new URL(request.url);

  // Render Angular vers une chaîne HTML
  const html = await renderApplication(bootstrap, {
    document: '<app-root></app-root>',
    url: url.pathname + url.search,
  });

  return new Response(html, {
    status: 200,
    headers: {
      'Content-Type':  'text/html; charset=utf-8',
      'Cache-Control': 's-maxage=600, stale-while-revalidate=3600',
    },
  });
}

Vérifier le runtime déployé

# Inspecter les headers de réponse pour confirmer le runtime
curl -I https://my-app.vercel.app/blog/article-1

# HTTP/2 200
# x-vercel-cache: HIT
# x-vercel-id: cdg1::abc123-1715000000000-abc
# server: Vercel
#
# La région cdg1 = Paris (edge)
# Si vous voyez iad1::lambda::... → c'est une lambda Node, pas edge
Quand préférer Node vs Edge ? Edge est imbattable sur les routes courtes (HTML < 200KB, latence critique, géolocalisation utile). Node reste préférable pour les routes qui font appel à des libs lourdes (PDF generation, image processing, ORMs SQL). Mesurez toujours avec Vercel Speed Insights avant de décider.

Variables d'environnement Vercel

Vercel sépare les variables d'environnement en 3 scopes : Development (local via vercel dev), Preview (chaque pull request) et Production (branche main déployée sur le domaine principal). Cette séparation est essentielle pour Angular SSR : la clé API utilisée par votre HttpClient doit changer selon l'environnement.

Définir une variable depuis la CLI

# Ajouter une variable production
vercel env add API_BASE_URL production
# ? What's the value of API_BASE_URL? https://api.example.com

# Idem pour preview et development
vercel env add API_BASE_URL preview
vercel env add API_BASE_URL development

# Lister toutes les variables
vercel env ls

# Récupérer les variables localement (génère .env.local)
vercel env pull

Exposer la variable côté Angular

Angular ne lit pas process.env au runtime côté navigateur — il faut injecter les valeurs au moment du build dans environment.ts. Vercel facilite ce pattern grâce à un script de génération exécuté avant ng build.

// scripts/generate-env.js — exécuté avant le build
// Lit les variables Vercel et écrit src/environments/environment.prod.ts
import { writeFileSync } from 'fs';
import { join }          from 'path';

const targetPath = join('src', 'environments', 'environment.prod.ts');

const envContent = `// Fichier généré automatiquement — NE PAS éditer
export const environment = {
  production: true,
  apiBaseUrl: '${process.env.API_BASE_URL || ''}',
  sentryDsn:  '${process.env.SENTRY_DSN  || ''}',
  // Variables Vercel système (toujours disponibles)
  vercelEnv:    '${process.env.VERCEL_ENV    || 'development'}',
  vercelRegion: '${process.env.VERCEL_REGION || 'local'}',
  gitCommitSha: '${process.env.VERCEL_GIT_COMMIT_SHA || ''}',
};
`;

writeFileSync(targetPath, envContent);
console.log('environment.prod.ts généré pour', process.env.VERCEL_ENV);
// package.json — chaîner generate-env avant le build
{
  "scripts": {
    "build":         "node scripts/generate-env.js && ng build",
    "vercel-build": "node scripts/generate-env.js && ng build --configuration=production"
  }
}

Variables système Vercel utiles

Variable Valeurs possibles Cas d'usage
VERCEL_ENV development | preview | production Désactiver analytics en preview
VERCEL_URL my-app-abc123.vercel.app URL canonique pour Open Graph
VERCEL_GIT_COMMIT_SHA abc123def456 Tag de version Sentry / DataDog
VERCEL_REGION cdg1 | iad1 | sfo1... Routing data center le plus proche

Preview Deployments — un environnement par PR

# Workflow GitHub → Vercel automatique
# 1. Vous poussez une branche feat/new-header
# 2. Vercel détecte la PR et déploie automatiquement
# 3. URL générée : https://my-app-feat-new-header-team.vercel.app
# 4. Bot GitHub commente la PR avec l'URL preview
# 5. Lors du merge, Vercel promote en production

# Pour récupérer l'URL preview en script CI :
vercel ls my-app --token=$VERCEL_TOKEN | head -2
Les Preview Deployments consomment du compute mais bénéficient d'un cache global Vercel. Leur principal intérêt est de tester SSR sur l'infrastructure réelle avant le merge — bien plus représentatif qu'un localhost:4000.

Performance et Lighthouse avant/après

La promesse du SSR Angular sur Vercel est concrète : voici les mesures Lighthouse réalisées sur une application de blog avec ~150 articles, mesurées en mode incognito sur Chrome 130 et connexion 4G simulée (slow 4G, 1.6 Mbps download, 750ms RTT).

Comparatif Lighthouse — page article

Métrique CSR (avant) SSR Vercel (après) Gain
Performance Score 62 / 100 96 / 100 +34 pts
First Contentful Paint 3.2 s 0.7 s −78%
Largest Contentful Paint 4.1 s 1.1 s −73%
Time to Interactive 5.8 s 2.4 s −59%
Total Blocking Time 620 ms 180 ms −71%
Cumulative Layout Shift 0.12 0.02 −83%
SEO Score 91 / 100 100 / 100 +9 pts

Optimiser l'hydration avec @defer

Angular 17+ introduit le bloc @defer qui charge un composant lazy uniquement à la demande (au scroll, au hover, au click). Combiné au SSR, ce mécanisme permet de réduire le bundle critique tout en gardant le HTML serveur complet pour les crawlers.

// article-detail.component.ts — template
@Component({
  template: `
    <article>
      <h1>{{ article().title }}</h1>
      <p class="lead">{{ article().excerpt }}</p>

      <!-- Contenu critique : présent dans le HTML SSR -->
      <div [innerHTML]="article().contentHtml"></div>

      <!-- Section commentaires : chargée seulement au scroll -->
      @defer (on viewport) {
        <app-comments [articleId]="article().id" />
      } @placeholder {
        <p class="text-muted">Chargement des commentaires...</p>
      } @loading (after 100ms) {
        <div class="spinner-border" role="status"></div>
      }

      <!-- Articles liés : chargés au hover de la zone -->
      @defer (on hover) {
        <app-related-articles [tag]="article().tag" />
      }
    </article>
  `,
})
export class ArticleDetailComponent { /* ... */ }

Vercel Speed Insights — monitoring continu

# Installer le client Speed Insights
npm install @vercel/speed-insights

# Activer dans app.config.ts (côté client)
# src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { injectSpeedInsights } from '@vercel/speed-insights';

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: 'SPEED_INSIGHTS_INIT',
      useFactory: () => {
        if (typeof window !== 'undefined') {
          injectSpeedInsights({
            framework: 'angular',
            sampleRate: 1, // 100% en dev, baisser en prod fort trafic
          });
        }
        return null;
      },
    },
  ],
};
Real User Monitoring vs Lab Tests : Lighthouse mesure en lab avec une machine virtuelle constante. Vercel Speed Insights mesure les Core Web Vitals chez vos vrais utilisateurs (RUM), incluant connexions lentes et appareils anciens. Les deux sont complémentaires : Lighthouse pour debugger, Speed Insights pour suivre l'impact business.

Vercel vs Netlify vs Cloudflare Pages

Trois plateformes dominent l'hébergement de frameworks SSR JavaScript en 2026. Voici un comparatif factuel pour Angular SSR spécifiquement, basé sur des déploiements réels d'une même application Angular 19.

Critère Vercel Netlify Cloudflare Pages
Plan gratuit (compute) 100 GB-h / mois 125 000 invocations 100 000 req/jour (Workers)
Plan payant entrée 20$/user/mois 19$/user/mois 5$/mois flat (Workers Paid)
Bandwidth gratuit 100 GB 100 GB Illimité (CDN)
Edge runtime cold start ~10 ms (V8 isolates) ~50 ms (Edge Functions) ~5 ms (Workers V8)
Node serverless cold start 200–500 ms 300–700 ms n/a (Workers only)
Adapter Angular officiel Oui (auto-detect) Oui (@netlify/angular-runtime) Manuel (wrangler.toml)
Preview deploy par PR Auto (GitHub/GitLab) Auto Auto (depuis 2024)
ISR / cache CDN stale-while-revalidate natif On-demand revalidation KV / Cache API
Régions edge 30+ régions ~13 régions 300+ POPs
Documentation Angular Excellente Bonne Limitée (community)

Recommandation par profil

  • Startup / projet persoVercel Hobby : DX imbattable, doc Angular parfaite, plan gratuit généreux
  • Équipe produit (3–10 dev)Vercel Pro : analytics, observability, RBAC, support prio
  • Trafic massif & budget serréCloudflare Pages : bandwidth illimité, Workers à 5$/mois
  • Écosystème Jamstack étendu (form, identity)Netlify : excellente intégration de plugins (forms, identity, large media)
Les trois plateformes supportent le déploiement automatique depuis GitHub avec preview par PR. La principale différence pour Angular SSR est la maturité de l'adapter et la documentation officielle. En 2026, Vercel reste l'option la plus documentée et la moins risquée pour un nouveau projet Angular SSR.

Conclusion

Déployer une application Angular SSR sur Vercel n'a jamais été aussi accessible. La commande ng add @angular/ssr génère toute l'infrastructure serveur, un vercel.json de quelques lignes pilote build et runtime, et le hybrid prerender/SSR/edge permet d'optimiser chaque route selon son profil de trafic. À la clé : un score Lighthouse au-dessus de 95, un FCP sous 1 seconde et une indexation Google immédiate.

Au-delà de la performance brute, Vercel apporte un environnement de développement industriel : Preview Deployments par pull request, Speed Insights pour le monitoring real-user, et une intégration GitHub native qui supprime les frictions du cycle CI/CD. Pour la majorité des projets Angular en 2026, c'est la voie la plus courte vers une production fluide.

Checklist mise en production Angular SSR sur Vercel :
  • Angular 17+ avec @angular/ssr installé via ng add
  • vercel.json avec functions et rewrites définis
  • app.routes.server.ts avec stratégie par route (Prerender / Server / Client)
  • Variables d'env séparées Development / Preview / Production
  • Cache CDN configuré (stale-while-revalidate) pour les routes ISR
  • Edge runtime testé sur les routes courtes critiques (latence < 50ms)
  • Vercel Speed Insights activé pour mesurer les Core Web Vitals RUM
  • Webhook Deploy Hook configuré si publication via CMS externe
  • Lighthouse score ≥ 90 vérifié sur 3 routes critiques (home, article, search)
  • @defer utilisé sur les sections lourdes (commentaires, articles liés)
  • Open Graph et Twitter Cards générés côté serveur via Meta service
  • Sentry ou DataDog connecté avec VERCEL_GIT_COMMIT_SHA comme release tag

Partager