Angular

SSR : éviter de transférer une réponse personnelle au navigateur

Décider quoi exclure du cache de transfert, vérifier le HTML réellement envoyé et typer la réponse sans confondre TypeScript et validation runtime.

Angular SSR produit un HTML visible et un transfer cache sérialisé distinct; exclure du cache n’efface pas le HTML.

Une réponse liée à la session peut se retrouver dans le HTML reçu par le navigateur. En SSR, Angular peut transférer des réponses HttpClient pour éviter une seconde requête pendant l’hydratation. Il faut décider quelles réponses peuvent être sérialisées et contrôler le HTML envoyé.

Le problème reste le même entre versions : une donnée ne doit pas traverser une frontière de session parce qu’un cache de transfert l’a rendue pratique. Les noms d’API et les options sont versionnés. L’exemple de configuration ci-dessous est vérifié dans la documentation Angular v20; l’API est également présente dans les références v19 et courante. Vérifiez toujours la signature de la version installée.

Préparer une route et un marqueur de test

Dans une application Angular SSR avec HttpClient et hydratation, relevez la version Angular installée et choisissez une route de test que vous contrôlez, par exemple /profile-test. Faites-lui appeler l’endpoint de profil avec une donnée fictive reconnaissable, telle que PROFILE_TEST_42, dans displayName. Utilisez uniquement un environnement et des données autorisés pour ce contrôle. Si cette route ne déclenche pas l’appel pendant le rendu serveur, elle ne permet pas d’examiner son transfert SSR.

Avant de modifier le filtre, suivez le contrôle du document décrit en fin d’article. Vous pourrez comparer le même appel et le même marqueur après la correction.

Configurer une exclusion globale par endpoint

Dans la configuration de l’application, withHttpTransferCacheOptions permet de fournir un filtre global aux requêtes admissibles. L’exemple ci-dessous exclut les réponses de profil, supposées propres à l’utilisateur :

import { HttpClient, HttpRequest, provideHttpClient } from '@angular/common/http';
import { ApplicationConfig } from '@angular/core';
import {
  provideClientHydration,
  withHttpTransferCacheOptions,
} from '@angular/platform-browser';

function mayTransfer(request: HttpRequest<unknown>): boolean {
  const apiOrigin = 'https://api.example.test';
  const url = new URL(request.url, apiOrigin);
  const isProfile = url.pathname === '/api/profile'
    || url.pathname.startsWith('/api/profile/');
  return !(url.origin === apiOrigin && isProfile);
}

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    provideClientHydration(
      withHttpTransferCacheOptions({
        filter: mayTransfer,
      }),
    ),
  ],
};

Pour l’origine https://api.example.test, le filtre refuse /api/profile, /api/profile?view=short et /api/profile/summary, mais laisse passer /api/profile-other. La comparaison de pathname ignore la query; celle d’origin limite cette règle à l’API désignée. Ici, une URL relative est résolue sur cette origine : remplacez la base par celle réellement utilisée pour ces requêtes, et ajoutez les autres origines sensibles à la politique si nécessaire. Le filtre refuse ces routes de profil au cache de transfert. Le type HttpRequest<unknown> rend son contrat explicite; le prédicat reste testable à part. Un simple includes() peut viser trop large ou manquer une variante d’URL. Adaptez les origines, chemins et règles d’authentification à l’application.

Typez aussi la réponse au point d’appel pour que le code consommateur exprime le contrat attendu :

interface ProfileSummary {
  displayName: string;
  plan: 'free' | 'pro';
}

function loadProfileSummary(http: HttpClient) {
  return http.get<ProfileSummary>('/api/profile/summary', {
    transferCache: false,
  });
}

Le générique aide TypeScript à détecter les usages incohérents du modèle. Il ne vérifie pas la forme du JSON à l’exécution. Si l’API peut renvoyer une structure inattendue, validez la donnée à la frontière avant de l’utiliser.

Cette exclusion ne retire pas une donnée déjà affichée par le template du HTML SSR. Elle empêche seulement son entrée dans le TransferCache. Vérifiez aussi les autres mécanismes de sérialisation utilisés par l’application.

Pour exclure une requête particulière, transferCache: false exprime l’intention directement au point d’appel :

this.http.get('/api/account/private-summary', {
  transferCache: false,
});

Cette option traite une exception locale. Lorsque plusieurs routes sensibles existent, définissez une politique globale. La règle de transfert HTTP et la politique de cache du document HTML sont distinctes.

Où passe la réponse ?

API appelée par le rendu serveur
              │
              ▼
        réponse HttpClient ──────────────► template SSR
              │                               │
       admissible / exclue                    │ données rendues
              │                               │
    si admissible seulement                    │
              ▼                               ▼
  entrée TransferCache sérialisée     HTML initial envoyé
              └──────────────────────► [markup rendu + état
                                        TransferCache éventuel]
                                                   │
                                                   ▼
                                          navigateur reçoit HTML
                                                   │
                                                   ▼
                                           hydratation Angular
                                                   │
                                                   ▼
                                HttpClient réutilise l'entrée sérialisée
                                si elle existe et est admissible

Le transfert concerne les réponses admissibles utilisées pendant l’initialisation : elles sont sérialisées pour être réutilisées à l’hydratation. Pour examiner une donnée sensible, vérifiez deux sorties distinctes : le balisage rendu (texte ou attributs) et l’état de transfert sérialisé. Vérifiez ensuite séparément les en-têtes de cache du document HTML, qui relèvent d’une autre couche.

Vérifier ce qui part réellement au navigateur

Ouvrez les outils de développement du navigateur, onglet Network, puis rechargez complètement /profile-test. Sélectionnez la requête de type Document correspondant à cette route et ouvrez Response. Examinez cette réponse initiale, plutôt que le DOM de l’onglet Elements, déjà modifiable par l’hydratation.

Cherchez PROFILE_TEST_42 à deux endroits distincts :

  • Dans le balisage rendu, par exemple le texte d’un élément ou un attribut : si le marqueur y figure, le template SSR expose la donnée. Une exclusion du transfert ne l’effacera pas; modifiez ce rendu si la donnée ne doit pas être envoyée.
  • Dans l’état sérialisé servant au transfert HTTP : si la réponse profil s’y trouve, vérifiez que le filtre reçoit l’URL attendue et couvre son origine et son chemin, ou utilisez l’exclusion au point d’appel.

Recommencez après la modification avec les mêmes données fictives. Si le marqueur disparaît du transfert mais reste dans le balisage, vous avez corrigé une seule des deux voies. Si vous ne le trouvez nulle part, confirmez que l’appel serveur a bien eu lieu et que la réponse contient ce marqueur; son absence seule ne prouve pas la confidentialité générale de l’application. Dans Headers, contrôlez ensuite les en-têtes HTTP du document, qui régissent une autre couche de cache.

Les références Angular v19, v20 et courante consultées documentent ce modèle SSR/hydratation et l’option de filtrage montrée ici. Vérifiez la signature de l’API dans la version de votre application avant de reprendre l’exemple. Le cache du navigateur, d’un CDN ou d’un proxy reste à contrôler séparément.

Sources techniques

  1. Server-side and hybrid-rendering — Caching data when using HttpClientAngular / Google
  2. withHttpTransferCacheOptionsAngular / Google
  3. TransferStateAngular / Google
  4. withHttpTransferCacheOptions — API Angular actuelleAngular / Google
  5. withHttpTransferCacheOptions — API Angular v19Angular / Google