06 août 2025·8 min de lecture

Mise en cache CDN pour Next.js : en‑têtes de cache sans fuite de données utilisateur

La mise en cache CDN pour Next.js accélère pages et ressources, mais de mauvais en‑têtes peuvent mettre en cache des données utilisateur. Apprenez quoi mettre en cache, des exemples d’en‑têtes et les pièges à éviter.

Mise en cache CDN pour Next.js : en‑têtes de cache sans fuite de données utilisateur

Pourquoi la mise en cache CDN peut casser une application Next.js

Les CDN rendent les sites plus rapides en sauvegardant des copies de ce que votre serveur envoie, puis en servant ces copies depuis un endroit proche du visiteur. Bien fait, ça réduit le temps de chargement et le coût serveur. Mal fait, ça peut casser les connexions, afficher de mauvaises données, ou sembler se résoudre "au hasard" plus tard.

Un CDN peut mettre en cache deux types de choses :

  • Des fichiers (images, JS, CSS)
  • Des réponses HTTP complètes (HTML d'une page, JSON d'une route API)

Le problème, c'est qu'une application Next.js sert souvent à la fois des pages publiques et des pages spécifiques à un utilisateur depuis le même domaine. Si votre CDN les traite de la même manière, vous risquez de mettre en cache quelque chose qui ne doit jamais être partagé.

Deux problèmes causent la plupart des maux de tête :

  • Contenu périmé : vous mettez à jour une page, mais les gens voient encore l'ancienne version parce qu'elle est en cache.
  • Réponses personnalisées mises en cache : le CDN enregistre une réponse destinée à une personne, puis la sert à quelqu'un d'autre.

Un échec courant ressemble à ceci : vous avez une page d'accueil marketing et un tableau de bord à /app. Un utilisateur se connecte, visite /app, et le serveur renvoie un HTML qui inclut son nom et son activité récente. Si cette réponse HTML est mise en cache, le visiteur suivant qui charge /app peut voir le tableau de bord du premier utilisateur, ou se retrouver coincé dans une boucle d'authentification cassée.

Donc la mise en cache CDN pour Next.js n'est pas simplement « activer le cache », c'est définir des règles claires. Décidez ce qui est sûr à mettre en cache (généralement les ressources statiques et les pages vraiment publiques), ce qui ne doit jamais être mis en cache (HTML personnalisé, flux d'authentification et la plupart des API utilisateur), et combien de temps le contenu en cache doit vivre.

Cartographiez votre app en parties cachables et non cachables

Avant de toucher aux en‑têtes, identifiez ce que votre application sert réellement. La mise en cache CDN fonctionne mieux quand vous traitez chaque type de réponse différemment. Une règle unique pour tout est la façon dont des données utilisateur se retrouvent en cache par erreur.

Commencez par lister les principaux types de réponses que vous renvoyez :

  • Fichiers statiques (JS, CSS, polices)
  • Images (y compris les variantes optimisées)
  • Pages HTML (marketing, docs, tableaux de bord)
  • Réponses d'API (JSON, webhooks, callbacks d'auth)

Ensuite, regroupez vos routes selon leur niveau de publicité réelle :

Groupe de routesExemplesPeut être mis en cache par le CDN ?Change souvent ?
Public/, /pricing, /blog/*Généralement ouiParfois
Semi-public/account, /checkoutGénéralement nonSouvent
Privé/dashboard, /adminNonSouvent

Marquez ensuite les zones à "fort churn". Les prix, les stocks et tout ce qui affiche un état en direct (commandes, utilisation, messages) deviennent rapidement périmés. Les tableaux de bord sont l'exemple évident, mais les pages de checkout, les étapes d'onboarding, et même des blocs "Bon retour" peuvent être personnalisés.

Les différents types de contenu nécessitent des règles différentes car les risques varient. Mettre en cache un bundle JS de façon agressive est généralement sans risque. Mettre en cache du HTML qui dépend des cookies peut faire fuiter la page d'un utilisateur vers un autre.

Un exemple réaliste : un site a une page d'accueil marketing publique, mais l'en‑tête affiche le nom de l'utilisateur lorsqu'il est connecté. Ce détail transforme une page par ailleurs cachable en une page risquée à moins de séparer l'expérience (coque publique + données privées chargées séparément) ou de désactiver la mise en cache quand des cookies sont présents.

Mettre en cache les ressources statiques en toute sécurité (JS, CSS, polices, images)

Les ressources statiques sont le meilleur gain car elles ne changent généralement pas selon l'utilisateur. Pensez aux fichiers téléchargés par le navigateur et réutilisés sur plusieurs pages.

Les ressources statiques typiques incluent :

  • Les fichiers build Next.js sous /_next/static/ (chunks JS, CSS)
  • Polices et fichiers d'icônes (woff2, ttf, svg)
  • Images et favicons
  • Tout ce qui se trouve dans votre dossier public/

Pour les fichiers versionnés (ceux avec un hash dans le nom), une mise en cache longue est la bonne valeur par défaut. Le hash change quand le contenu change, donc le fichier est effectivement immutable.

Un en‑tête courant pour ceux‑ci est :

Cache-Control: public, max-age=31536000, immutable

Avant de définir un TTL d'un an, assurez‑vous que l'« immutable » est vraiment vrai dans votre processus. Si vous servez logo.png depuis public/ et que vous remplacez plus tard le fichier en gardant le même nom, un long TTL peut garder l'ancien logo dans les caches longtemps. Pour des noms non versionnés, gardez des TTLs plus courts ou ajoutez une étape de build qui fingerprint les noms de fichiers.

Les images ont souvent leur propre stratégie. Si vous générez plusieurs tailles ou formats (comme WebP et AVIF), la mise en cache est sûre seulement si l'URL finale identifie de façon unique la sortie. Si un endpoint d'image optimisée varie selon la largeur, la qualité ou le format, ces entrées doivent faire partie de la clé de cache. Sinon, les utilisateurs peuvent recevoir la variante incorrecte.

Un contrôle rapide : prenez un chunk JS et une police, ouvrez‑les et confirmez qu'ils ne changent jamais sauf lors d'un nouveau déploiement.

Mettre en cache les pages et le HTML : ce qui est sûr et ce qui est risqué

Le plus gros gain (et le plus grand risque) vient de la mise en cache du HTML complet. Le HTML est ce que l'utilisateur voit, donc si vous mettez en cache la mauvaise chose, vous pouvez afficher la mauvaise information à la mauvaise personne.

Le HTML sûr à mettre en cache est généralement le contenu identique pour tout le monde : pages marketing, tarification, documentations, pages publiques de blog. Un test simple : ouvrez la page en navigation privée. Si elle ressemble à la même chose quand on est connecté et déconnecté, c'est un bon candidat.

Le HTML risqué à mettre en cache est tout ce qui change par utilisateur, session ou selon des données privées : tableaux de bord, paramètres de compte, checkout, historique de commandes, pages affichant un nom, un e‑mail, des éléments sauvegardés, l'état de facturation ou "vous vous êtes connecté à ...". Mettre cela en cache au niveau CDN conduit aux fuites de données.

Règle pratique :

  • Mettez en cache les pages publiques avec des durées contrôlées.
  • Ne mettez pas en cache les pages utilisateur sauf si vous êtes sûr à 100 % que la réponse est identique pour tous.
  • En cas de doute, traitez le HTML comme privé et désactivez la mise en cache.

L'ISR (Incremental Static Regeneration) se situe entre les deux. Il permet à Next.js de servir une page mise en cache rapidement, puis de la rafraîchir en arrière‑plan selon un minuteur. C'est idéal pour des pages comme des billets de blog qui changent occasionnellement. Ce n'est pas adapté aux pages personnelles, car la fraîcheur ne résout pas le problème du "mauvais utilisateur".

Un autre piège : mettre en cache le HTML n'est pas la même chose que mettre en cache les appels de données. Une page peut être sûre à mettre en cache alors qu'une API qu'elle appelle ne l'est pas. Ou vous pouvez garder le HTML privé mais cacher des données vraiment publiques (comme une liste de produits) avec un TTL court. Tout mélanger conduit à des situations où "ça marchait en staging" devient du contenu périmé ou une exposition accidentelle.

En‑têtes de cache à connaître (sans le jargon)

La plupart des problèmes de cache viennent d'une chose : le CDN et le navigateur devinent combien de temps quelque chose est sûr à réutiliser. Les en‑têtes de cache vous disent cela clairement.

Cache‑Control : le principal

Cache-Control est un ensemble d'instructions. Les parties les plus communes sont :

  • max-age=... : combien de temps le navigateur peut le garder (en secondes)
  • s-maxage=... : combien de temps les caches partagés (un CDN) peuvent le garder. Si présent, les CDN suivent généralement celui‑ci plutôt que max-age.
  • public : ok à stocker dans un cache partagé
  • private : seul le navigateur de l'utilisateur peut le stocker (un CDN ne devrait pas)
  • no-store : ne pas stocker nulle part (à utiliser pour les réponses personnelles ou sensibles)

Règle simple : si la réponse peut différer selon l'utilisateur, évitez le public. Pour les tableaux de bord et pages de compte, choisissez par défaut no-store sauf raison très spécifique.

stale-while-revalidate : servir de l'ancien pendant qu'on rafraîchit

stale-while-revalidate=... permet à un cache de servir une version légèrement ancienne pendant qu'il récupère une nouvelle version. Cela fonctionne bien pour le contenu qui peut être un peu en retard (comme une page marketing). C'est risqué pour tout ce qui doit être exact immédiatement (facturation, permissions).

ETag et Last-Modified : comment les caches vérifient si quelque chose a changé

Avec ETag ou Last-Modified, un cache peut demander « est‑ce que ça a changé ? ». Si non, le serveur répond avec une petite réponse "not modified" au lieu de renvoyer tout le corps.

C'est utile quand les réponses sont volumineuses, que le contenu se met à jour souvent, ou que vous voulez des mises à jour rapides sans désactiver la mise en cache.

Vary : l'étiquette "dépend de"

Vary indique aux caches quels en‑têtes de requête peuvent changer la réponse. Ça compte quand les cookies ou l'authentification sont impliqués.

Si le HTML change selon l'état de connexion, et que vous ne gérez pas correctement Vary + règles de cache, un CDN peut servir la version d'un utilisateur à un autre. Des en‑têtes Vary courants incluent Cookie, Authorization et Accept-Encoding.

Pas à pas : définir des règles de cache pour une app Next.js typique

Refactoriser pour la performance en toute sécurité
Nettoyez le code pour que les pages publiques se mettent en cache correctement et que les routes privées restent non mises en cache.

Une approche pratique est de commencer par les éléments les plus sûrs, puis d'aller vers le contenu qui peut changer.

1) Commencez par les cibles les plus sûres

Commencez par les fichiers identiques pour tous : bundles JavaScript, CSS, polices et images versionnées. Ce sont à faible risque et donnent souvent le plus grand gain de vitesse.

2) Définissez les durées de cache selon ce qui peut changer

Utilisez une mise en cache très longue pour les assets immuables (fichiers dont le nom change quand le contenu change). Pour les pages et le HTML, utilisez des mises en cache plus courtes, car le contenu peut être mis à jour sans changement de nom de fichier.

Une checklist qui tient dans la vraie vie :

  • Commencez par /_next/static/*, vos assets clairement versionnés, et autres fichiers avec noms hachés.
  • Pour les assets immuables, envoyez un long Cache-Control avec immutable.
  • Pour les pages publiques non personnalisées (marketing, docs), utilisez un cache partagé plus court (minutes à heures) et envisagez stale-while-revalidate.
  • Pour tout ce qui est spécifique à un utilisateur (tableaux de bord, pages de compte, routes API dépendant des cookies), forcez Cache-Control: private ou no-store.
  • Décidez comment vous purgerez le cache : comptez sur des déploiements qui changent les noms de fichiers, et ayez un plan clair de purge pour le HTML quand vous publiez des mises à jour urgentes.

3) Testez avant d'y faire confiance

Ne vous arrêtez pas à "c'est rapide". Faites un refresh complet et comparez le comportement connecté vs déconnecté.

Un contrôle de réalité rapide : ouvrez la même page en navigation privée et dans une session connectée. Si le HTML, les en‑têtes ou les données visibles correspondent alors qu'ils ne devraient pas, considérez‑le comme une fuite et arrêtez de mettre en cache cette route.

Clés de cache CDN et règles qui décident ce qui est servi

Un CDN ne "met pas en cache une page" de façon abstraite. Il met en cache une réponse sous une clé de cache. Cette clé est généralement construite à partir du chemin de la requête, et parfois de la query string, des en‑têtes ou des cookies.

Si la clé est trop large, les utilisateurs voient le mauvais contenu. Si elle est trop spécifique, vous avez un faible taux de hits et des pages plus lentes.

Ce qui appartient habituellement à la clé de cache

Commencez simplement : pour les pages publiques, le chemin suffit souvent. Ajoutez d'autres éléments seulement s'ils changent vraiment le contenu que l'utilisateur doit voir.

  • Chemin : /pricing vs /blog/slug doivent être séparés.
  • Params de query : incluez seulement ceux qui changent le contenu (par exemple ?page=2). Ignorez les paramètres de tracking comme utm_*.
  • En‑têtes : incluez seulement les variantes réelles (par exemple la langue).
  • Cookies : évitez d'utiliser les cookies pour le contenu public. Ils multiplient les variantes de cache.

Le cas dangereux est quand un CDN met en cache une réponse personnalisée parce qu'un cookie était présent. Même si le cookie n'est pas dans la clé, mettre en cache une réponse personnalisée peut tout de même provoquer une fuite de données.

Variantes : locale et appareil

Si votre HTML change selon la locale, utilisez un signal clair et variez dessus (souvent Accept-Language, ou un en‑tête dédié que vous contrôlez).

Faites attention à la variation selon l'appareil. Varier sur User-Agent crée trop de versions et se prête facilement à l'erreur. Préférez le responsive design, ou un petit en‑tête explicite si vous avez vraiment besoin d'un HTML séparé.

Enfin, définissez des règles de contournement pour tout ce qui ne doit jamais être mis en cache : /admin, les routes /api qui renvoient des données personnelles, les tableaux de bord authentifiés et toute route qui vérifie un cookie de session.

Comment éviter de mettre en cache des réponses personnalisées

Interventions rapides
La plupart des corrections sont livrées en 48 à 72 heures après identification des problèmes avec vous.

Un CDN est génial jusqu'à ce qu'il enregistre accidentellement une réponse destinée à une personne et la serve à quelqu'un d'autre. C'est le risque majeur : du HTML ou du JSON mis en cache peut devenir une fuite si ça inclut un nom, un e‑mail, l'état de facturation ou toute donnée liée à une session connectée.

La personnalisation s'infiltre généralement via les cookies et les en‑têtes d'auth. Si une requête contient Cookie ou Authorization, supposez qu'elle peut produire une sortie spécifique à un utilisateur, sauf si vous êtes absolument certain du contraire.

Utilisez des en‑têtes de cache clairs pour tout ce qui est derrière une connexion. Pour les tableaux de bord, paramètres, pages de facturation et routes API utilisateur, préférez Cache-Control: no-store (ou au moins private, no-store). Cela indique aux navigateurs et aux CDN de ne pas conserver de copie.

Signaux pratiques qui doivent vous pousser à ne pas mettre en cache :

  • La requête inclut Cookie, Authorization ou un en‑tête de token de session.
  • La réponse dépend de l'identité de l'utilisateur (même si l'URL est identique).
  • Le HTML inclut des données de compte, activité récente ou état de facturation.
  • Une API renvoie des enregistrements utilisateur, des tokens, ou toute information sensible.
  • Vous ne pouvez pas expliquer votre clé CDN en une phrase.

Les pages mixtes sont délicates. Un schéma courant est une coque marketing publique qui affiche aussi "Salut Sam" ou un compte de notifications quand l'utilisateur est connecté. Si le HTML est personnalisé, ne le mettez pas en cache publiquement. Une solution plus sûre : mettez en cache la coque publique, puis récupérez les fragments personnalisés côté client (ou depuis un endpoint non cachable séparé).

Si possible, séparez les endpoints publics et privés pour garder vos règles simples. Gardez les routes cachables clairement publiques, et les routes authentifiées clairement non cachables.

Erreurs courantes qui causent du contenu périmé ou des fuites de données

La plupart des échecs de cache arrivent quand quelque chose "semblait rapide" lors d'un test rapide, mais se comporte différemment une fois de vrais utilisateurs connectés. Le danger n'est pas seulement les pages périmées. C'est aussi servir le contenu d'une personne à une autre.

Erreurs récurrentes :

  • Mettre en cache du HTML pour des routes nécessitant connexion parce que cela semblait sûr en test anonyme.
  • Marquer des réponses public alors qu'elles changent selon des cookies, un en‑tête d'auth ou la géolocalisation.
  • Donner un long max-age à des assets non versionnés (comme /app.css), puis publier une mise à jour et garder l'ancien fichier.
  • Oublier que les redirections, pages d'erreur et 404 peuvent aussi être mises en cache, ce qui peut figer une panne temporaire ou une route incorrecte.
  • Faire confiance au comportement par défaut de l'hébergeur ou du framework sans vérifier ce que le CDN stocke réellement.

Un moyen rapide de repérer un risque : demandez « Cette réponse pourrait‑t‑elle être différente pour deux utilisateurs ? ». Si oui, elle ne devrait pas être mise en cache publiquement. Même une petite différence comme une salutation signifie que le HTML est personnalisé.

Surveillez aussi la mise en cache invisible : une redirection 302 mise en cache vers /login, un 404 mis en cache pour une page nouvellement lancée, ou une erreur 500 mise en cache pendant un déploiement.

Vérifications rapides avant de mettre la mise en cache en production

Avant de déployer la mise en cache, faites des tests avec de vraies requêtes, pas seulement ce que vous supposez que le code fait. De petits oublis d'en‑têtes peuvent devenir des pages périmées ou, pire, la mauvaise personne voyant la mauvaise donnée.

Commencez par les basiques :

  • Les pages publiques identiques pour tous doivent envoyer Cache-Control: public et un s-maxage raisonnable pour que le CDN puisse les mettre en cache.
  • Tout ce qui est derrière une connexion doit être Cache-Control: private ou no-store.

Si vous êtes incertain, par défaut utilisez no-store et assouplissez ensuite.

Vérifications pré‑déploiement qui attrapent la plupart des problèmes :

  • Ouvrez une page publique et confirmez qu'elle est réellement cachable (Cache-Control: public avec s-maxage).
  • Ouvrez une page authentifiée et confirmez qu'elle n'est pas cachable par un cache partagé (private ou no-store, jamais public).
  • Vérifiez les assets statiques : une longue mise en cache n'est sûre que si les noms de fichiers sont versionnés (hash dans le nom). Sinon, gardez le cache court.
  • Testez les API qui renvoient des données utilisateur et confirmez qu'elles ne sont pas mises en cache par le CDN.
  • Testez deux comptes : connectez‑vous en tant que Compte A dans un navigateur, Compte B dans un autre, et chargez les mêmes URLs. Aucune donnée personnelle ne doit circuler entre les deux.

Surveillez aussi les query params. Si votre CDN met en cache par URL complète, ?ref=, ?utm_ et des filtres aléatoires peuvent créer des variantes de cache infinies. Décidez quels params doivent être ignorés et lesquels doivent faire partie de la clé de cache.

Exemple réaliste : site marketing + tableau de bord sur le même domaine

Sauver un Next.js généré par IA
Vous avez un codebase généré par IA ? Nous aidons à le rendre prêt pour la production avec des frontières de cache propres.

Une startup publie une app Next.js qui mélange un site marketing public et un tableau de bord connecté sous le même domaine. Le trafic monte après les lancements, donc ils ajoutent un CDN pour accélérer les pages.

Ils séparent l'app en deux groupes.

Mettre en cache ce qui est sûr et identique pour tous :

  • JS et CSS versionnés (long cache, car les noms changent au déploiement)
  • Images, polices et icônes (long cache si les noms sont versionnés ; plus court si on réécrit les fichiers)
  • Pages marketing comme /, /pricing, /blog (TTL CDN court pour que les modifications apparaissent rapidement)

Pour le HTML marketing, ils définissent un court TTL partagé et une petite fenêtre stale-while-revalidate. Le CDN peut servir une page légèrement ancienne pendant qu'il la rafraîchit.

Ne jamais mettre en cache ce qui varie selon l'utilisateur :

  • /dashboard et tout son sous‑arborescence
  • Pages de paramètres et de facturation
  • /api/user (et tout ce qui renvoie des données de compte)
  • Pages d'authentification et callbacks

Pour vérifier qu'ils n'ont pas créé de fuite, ils testent comme un client paranoïaque : deux comptes, deux navigateurs, refresh forcé et vérification rapide des en‑têtes. Les pages personnalisées ne doivent jamais afficher de directives public.

Quand quelque chose tourne mal (un utilisateur voit un mauvais nom, ou un état d'abonnement périmé), ils retirent d'abord la règle CDN. Puis ils resserrent les en‑têtes sur les routes risquées (commencer par /dashboard et /api) avant de réactiver toute mise en cache.

Étapes suivantes : déployer en toute sécurité et demander de l'aide si c'est un désordre

Commencez petit et rendez les gains ennuyeux. Mettez en cache les assets immuables (JS et CSS hachés, polices, images versionnées) avec des TTL longs. Une fois stable, ajoutez la mise en cache pour les pages vraiment publiques. Traitez tout ce qui peut varier selon l'utilisateur, un cookie ou une authentification comme non cachable, sauf si vous avez conçu explicitement le cache pour l'edge.

Notez vos règles de cache en clair. Beaucoup de bugs de cache apparaissent des mois plus tard quand quelqu'un ajoute un nouvel en‑tête, introduit une redirection ou change l'auth. Un petit "contrat de cache" facilite les revues : ce qui est cachable, ce qui ne doit jamais l'être, et quels signaux (cookies, en‑têtes, query params) changent la réponse.

Si vous avez hérité d'une base Next.js générée par IA, revérifiez les en‑têtes et les flux d'auth. Ces projets livrés par IA embarquent souvent des patterns de routage mixtes, des Cache-Control incohérents et des sessions qui fonctionnent localement mais craquent derrière un CDN.

Si vous n'arrivez pas à résoudre des authentifications cassées, des comportements de cache risqués, ou si vous ne pouvez pas expliquer ce que votre CDN stocke, FixMyMess (fixmymess.ai) réalise des diagnostics de code et des réparations pour les apps générées par IA, y compris le démêlage des en‑têtes de cache et des frontières d'auth. Ils offrent aussi un audit de code gratuit pour identifier les problèmes avant de toucher à la production.