Guide Next.js 16
Débogage du cache Next.js sur Vercel
Classifiez la couche de cache, lisez les en-têtes de cache Vercel, prouvez que la réponse est incorrecte, puis appliquez la reprise la plus étroite qui restaure Acme Shop sans purge large risquée.
Sur cette page
« Le cache est obsolète » peut décrire plusieurs problèmes : un ancien déploiement, une réponse HTTP publique mise en cache, un fetch Next.js tagué, une image optimisée ou des données qui n'ont jamais été mises en cache. La réponse sûre n'est pas de purger en premier. C'est d'identifier la couche, de conserver un marqueur de corps inoffensif, puis de choisir une reprise cohérente avec les preuves recueillies.
À l'échelle où opère une couche d'orchestration d'edge managée — de nombreuses routes et régions, plusieurs mécanismes de cache empilés devant une seule origine — cette discipline doit être reproductible, pas improvisée sous la pression d'un incident. Ce guide traite le débogage du cache Next.js comme un flux de décision fixe, appuyé par des preuves à chaque étape, plutôt que comme une série de commandes de purge lancées dans l'urgence.
Ne diagnostiquez jamais avec des données de production privées
Débuggez Acme Shop avec une URL de catalogue publique et un marqueur de révision inoffensif. Ne copiez pas d'en-têtes d'autorisation, de cookies, d'URL client, de secrets de contournement du cache ni de charges privées complètes dans des tickets, l'historique shell ou des logs partagés.
Suivre le flux de décision du débogage du cache Next.js
Utilisez une seule URL et une seule valeur attendue. Pour ce scénario, Acme Shop attend que /api/public-catalog contienne catalogRevision: "2026-07-14T10:00Z". Un statut de cache seul, sans ce contrôle de corps, n'est qu'un indice : nommez la révision publique attendue, capturez en-têtes et corps deux fois, confirmez le déploiement actif, classez la réponse comme un problème de CDN ou de données taguées, lisez la raison du statut et non seulement son état, puis validez ou récupérez de la manière la plus étroite possible. Ne purgez jamais lorsque le corps est déjà correct, et traitez toute exposition de données privées comme un incident d'isolation des données, pas comme un réglage de cache.
Un seul flux de décision, une seule surface d'observabilité
Le catalogue d'Acme Shop peut se trouver derrière Vercel aujourd'hui et derrière des
fournisseurs régionaux additionnels demain. Ce flux de décision reste identique dans
les deux cas ; seul l'endroit où l'on regarde change. MYO existe pour qu'Optimi puisse
corréler un transcript x-vercel-cache avec le reste de la pile edge dans une seule
vue, plutôt qu'une connexion et un vocabulaire d'en-têtes distincts par fournisseur en
plein incident.
Étape 1 : capturer les en-têtes de cache Vercel avant tout changement
Demandez la même URL publique deux fois. L'option -D - affiche les en-têtes de réponse et la sortie par défaut affiche le corps, produisant un transcript partageable sans identifiant privé :
curl -sS -D - https://acme-shop-preview.example/api/public-catalog
curl -sS -D - https://acme-shop-preview.example/api/public-catalog
Voici une sortie représentative pour la seconde requête publique. HIT, MISS, STALE, PRERENDER, REVALIDATED et BYPASS sont des états du CDN Vercel ; c'est la révision présente dans le corps qui décide si le résultat est correct :
HTTP/2 200
content-type: application/json
cache-control: public, max-age=0, must-revalidate
x-vercel-cache: HIT
x-vercel-id: cle1::iad1::abc123
{"catalogRevision":"2026-07-14T10:00Z","products":[{"slug":"solar-pack","name":"Solar Pack","priceCents":12900}]}
Pour une preview protégée, vercel curl (CLI v48.8.0+) et vercel httpstat (v48.9.0+, avec un httpstat installé localement) évitent de placer un secret de contournement dans la commande. Les deux sont en beta : gardez curl classique comme capture portable de référence.
vercel curl /api/public-catalog --deployment https://acme-shop-preview.example
vercel httpstat /api/public-catalog --deployment https://acme-shop-preview.example
Lire les en-têtes cache-control réellement envoyés par la fonction
curl -D - ne montre que ce qui atteint le client. Vercel gère trois en-têtes Cache-Control distincts, et l'un d'eux ne quitte jamais l'edge :
// Route Handler : trois directives ciblées, trois audiences différentes.
return new Response(JSON.stringify(catalog), {
headers: {
"content-type": "application/json",
"Cache-Control": "public, max-age=0",
"CDN-Cache-Control": "public, s-maxage=60",
"Vercel-CDN-Cache-Control": "public, s-maxage=3600, stale-while-revalidate=300",
},
})
Vercel-CDN-Cache-Control ne pilote que l'edge propre à Vercel ; il n'est jamais transmis au navigateur ni à un autre CDN. CDN-Cache-Control est renvoyé au navigateur et aux CDN en aval. Si une fonction omet CDN-Cache-Control, Vercel retire s-maxage et toute directive de rafraîchissement en arrière-plan du cache-control visible par le navigateur, et le remplace par public, max-age=0, must-revalidate — exactement l'en-tête du transcript ci-dessus. Un s-maxage absent dans une capture curl est donc attendu, pas la preuve que la route n'est pas cachée : faites confiance à x-vercel-cache et au corps plutôt qu'à la valeur cache-control côté navigateur.
Étape 2 : identifier la couche derrière les états de cache HIT MISS
Utilisez ce tableau avant toute invalidation :
| Symptôme | Couche probable | Première preuve à collecter |
|---|---|---|
| La réponse actuelle ne contient pas le commit ou la release attendus | Déploiement ou sortie de build | vercel inspect <url-de-déploiement> et la révision du corps |
Une réponse JSON publique complète est ancienne et x-vercel-cache indique HIT ou STALE | CDN Vercel | En-têtes, marqueur de corps, tag de cache et éligibilité de la réponse |
| Plusieurs pages affichent une ancienne valeur de catalogue | Cache de données Next.js | Tag fetch exact, enregistrement source, résultat de webhook et logs runtime |
| Une image reste ancienne après changement de sa source | Cache d'optimisation d'image | URL de l'image source et son workflow d'invalidation spécifique |
| Seuls les utilisateurs connectés observent le problème | Chemin de données privé ou logique applicative | Un compte de test contrôlé et les logs origine/application, pas un changement de cache partagé |
x-vercel-cache décrit l'état de réponse du CDN. Il peut afficher MISS pour une route dynamique alors qu'un fetch de données côté serveur a bien été mis en cache. Un marqueur de révision inoffensif dans le corps et les logs applicatifs constituent la preuve pour la couche de données.
Chaque paire d'états de cache HIT MISS porte aussi une raison, que Vercel affiche à côté de l'en-tête dans la section « Cache » du log runtime de la requête, et qui change l'action à mener :
| État | Raison à vérifier | Signification pour Acme Shop |
|---|---|---|
MISS | Cold | Première requête après un déploiement, ou entrée rarement sollicitée évincée. Attendu une fois, pas un bug. |
MISS | Request collapsed | Un pic de trafic a envoyé des requêtes identiques ; Vercel n'a exécuté qu'un seul fetch origine et a retenu les autres. Pas un défaut de cache. |
STALE | Tag-based invalidation | Un webhook a invalidé acme-shop:catalog-response ; cette entrée sert une dernière fois avant son rafraîchissement en arrière-plan. |
REVALIDATED | Tag-based deletion | L'entrée a été supprimée définitivement (dangerously-delete, ou revalidateTag sans durée de vie), donc cette requête a payé la latence origine complète au lieu de servir du stale puis un rafraîchissement. |
Étape 3 : inspecter le déploiement actif et le contrat de cache
Confirmez d'abord le déploiement qui sert le trafic, avant de modifier une règle de cache. Inspectez ensuite la réponse publique exacte et le fetch qui l'alimente :
vercel inspect https://acme-shop-production.example
vercel logs --environment production --query "public-catalog" --since 1h --expand
// Données publiques : persistance délibérée, fraîcheur de repli, cible d'invalidation connue.
await fetch("https://catalog.acme-shop.example/v1/products", {
cache: "force-cache",
next: { revalidate: 300, tags: ["acme-shop:catalog"] },
})
// Données privées : ne jamais en faire une réponse partagée.
await fetch("https://accounts.acme-shop.example/v1/me", {
cache: "no-store",
headers: { Authorization: `Bearer ${accessToken}` },
})
Ce motif fetch correspond au cache de données Next.js, et il continue de fonctionner en Next.js 16 tant que le projet n'a pas activé cacheComponents. Si Acme Shop active ensuite cacheComponents: true et fait passer la fonction de catalogue à "use cache: remote" avec cacheTag/cacheLife, ces données basculent vers le Runtime Cache de Vercel — vérifiez alors le panneau d'observabilité Runtime Cache du projet (lectures, écritures, taux de hit, revalidations par tag) en plus des logs, car le succès d'un webhook ne garantit plus que l'entrée taguée s'est rafraîchie dans cette région.
Vérifiez la présence de Set-Cookie, d'un Authorization de requête, de private, no-cache, no-store, Vary: *, de redirections, de codes de statut non pris en charge et de différences de corps : chacun de ces éléments explique pourquoi une réponse publique n'est pas cachable, et aucun ne doit être contourné pour améliorer une métrique. Une réponse ne devient éligible au cache CDN de Vercel que si : la requête est GET/HEAD sans en-tête Range ni Authorization ; le statut est 200, 404, 410, 301, 302, 307 ou 308 ; le corps fait moins de 10 Mo (20 Mo en streaming) ; et la réponse n'a ni Set-Cookie, ni Vary: *, ni directive private/no-cache/no-store. Une réponse qui échoue sur l'un de ces critères n'est pas mise en cache à juste titre — c'est un problème de contrat à corriger dans la route, pas une couche de cache à purger.
Étape 4 : utiliser la reprise la plus étroite
- Mauvais déploiement : restaurez le déploiement approuvé connu comme bon, puis répétez le même contrôle de corps.
- Mauvaise source ou données Next.js taguées : corrigez la source et déclenchez l'endpoint de revalidation authentifié et listé sur allowlist d'Acme Shop pour le tag et les chemins connus.
- Mauvaise réponse CDN publique : corrigez d'abord la sortie ou les en-têtes de la route. Une fois correcte, invalidez uniquement le tag de cache de cette réponse depuis une session de projet lié et revue.
- Image optimisée : invalidez l'image source spécifique ; ne purgez pas les réponses de catalogue ou de compte.
# Uniquement après correction de la réponse publique et revue du périmètre du tag.
vercel cache invalidate --tag acme-shop:catalog-response
# Plusieurs tags liés en un seul appel, séparés par des virgules, sans espace.
vercel cache invalidate --tag acme-shop:catalog-response,acme-shop:catalog-listing
# L'optimisation d'image se base sur l'URL de l'image source, pas sur le tag de la page.
vercel cache invalidate --srcimg /images/products/solar-pack.png
# À utiliser seulement quand une restauration de service immédiate exige la release antérieure approuvée.
vercel rollback https://acme-shop-known-good.example
vercel cache invalidate --tag marque simplement ce tag comme obsolète : la prochaine requête sert instantanément le corps stale et le rafraîchit en arrière-plan, sans coût de latence pour le visiteur — c'est le choix étroit et par défaut. dangerously-delete et une purge globale vercel cache purge --type cdn --yes se comportent volontairement autrement : ils suppriment l'entrée (ou tout le projet), donc la prochaine requête correspondante bloque sur un fetch au premier plan, et des requêtes simultanées pour la même entrée supprimée peuvent provoquer un thundering herd sur l'origine. Réservez-les au cas où une réponse stale est pire qu'une réponse lente, et délimitez le périmètre avec --tag ou --srcimg plutôt qu'avec une purge globale au projet.
Étape 5 : prouver la reprise et l'enregistrer
| Validation | Résultat attendu | Signal d'échec et réponse |
|---|---|---|
| Positive | La prochaine requête publique porte la révision attendue ; une requête ultérieure reste correcte et peut passer de STALE à HIT. | La révision reste ancienne : vérifiez la source de données, l'orthographe du tag et le chemin concerné avant une nouvelle invalidation. |
| Négative | /account reste dynamique/privé et aucune route publique ne contient de marqueur de compte de test. | Le moindre marqueur inter-compte : retirez l'éligibilité partagée, revenez en arrière si nécessaire, et traitez le cas comme un incident d'isolation des données. |
| Échec | Une erreur amont contrôlée est visible comme une réponse d'erreur ou une erreur applicative, jamais comme un faux catalogue frais. | Un succès en cache masque l'échec contrôlé : inspectez le TTL du CDN et la gestion d'erreur amont avant de restaurer le comportement de cache. |
Gardez l'enregistrement d'incident court mais complet : URL, révision attendue et observée, horodatages, en-tête de région, déploiement, périmètre de la commande, opérateur et validation finale. Joignez la ligne de la section « Cache » du log runtime pour la requête de validation plutôt que de la retranscrire. Ne consignez jamais de secrets ni de corps privés.
Étape 6 : prévenir le prochain incident
Transformez le diagnostic réussi en petit runbook :
- Attribuez un responsable, une cible de fraîcheur et une URL de test sûre à chaque route cachable.
- Émettez une révision de déploiement ou de contenu non sensible dans les réponses de test publiques.
- Conservez une correspondance entre les tags de données et les chemins rendus, en incluant les nouveaux tags Runtime Cache si
cacheComponentsest activé. - Alertez sur les échecs de webhooks et les erreurs de revalidation répétées.
- Testez un chemin authentifié chaque fois qu'une règle de cache ou un en-tête de réponse change.
- Examinez le comportement de cache depuis plusieurs régions représentatives pour les routes à fort trafic.
Dépannage rapide
| Symptôme | Cause | Reprise étroite |
|---|---|---|
x-vercel-cache: HIT mais le corps est incorrect | Une réponse valablement mise en cache contient une représentation source ancienne ou erronée. | Vérifiez la révision source, corrigez-la, puis invalidez uniquement acme-shop:catalog-response. |
x-vercel-cache: MISS mais le catalogue semble toujours ancien | Le CDN a manqué alors que le cache de données Next.js a fourni le résultat du fetch. | Inspectez le tag du fetch et les logs de webhook ; n'augmentez pas le TTL du CDN. |
La réponse navigateur ne montre pas s-maxage | La fonction a défini Cache-Control sans CDN-Cache-Control, donc Vercel retire les directives partagées avant de transmettre au navigateur. | Utilisez x-vercel-cache, le corps et la source de la route comme preuve, pas la valeur cache-control côté navigateur. |
| La preview fonctionne mais la production est ancienne | L'alias de production pointe vers un autre déploiement, ou l'environnement de production diffère. | Exécutez vercel inspect pour le déploiement de production et comparez révision et configuration. |
| Deux URL de déploiement renvoient des résultats différents pour la même requête | La clé de cache inclut l'URL unique du déploiement, donc chaque déploiement démarre avec son propre cache froid. | Comparez via l'alias de production, et attendez-vous à un MISS froid par déploiement. |
Une rafale de MISS juste après une invalidation, puis un retour au calme | Des requêtes concurrentes pendant la régénération en arrière-plan peuvent fusionner en un seul appel origine ; protecteur, pas cassé. | Confirmez que le corps est correct une fois la rafale retombée ; n'escaladez que s'il reste incorrect. |
vercel cache invalidate ne trouve pas le résultat attendu | La réponse n'a pas été émise avec ce tag, le projet n'est pas lié, ou le périmètre est erroné. | Vérifiez le tag dans la source et le projet lié ; utilisez le webhook pour les tags de données Next.js. |
| Une image reste stale après invalidation du tag de catalogue | L'optimisation d'image se cache par URL d'image source, pas par le tag de réponse de la page. | Utilisez vercel cache invalidate --srcimg <url-image-source> à la place. |
| Le développement local ne reproduit jamais la production | Le comportement fetch/HMR en développement diffère du comportement de cache déployé. | Reproduisez dans un déploiement preview avec la même URL publique et le même marqueur de corps. |
Références faisant autorité
- Vercel : diagnostiquer les problèmes de cache
- Vercel CDN Cache
- En-têtes de réponse Vercel
- Vercel : statut et raisons de cache
- Vercel : purger le cache CDN
- Next.js
revalidateTag
Transformez les états de cache HIT MISS en runbook reproductible
Optimi orchestre Performance, Sécurité et Visibilité sur l'ensemble de votre pile edge, avec MYO comme vue d'observabilité unique pour les en-têtes de cache Vercel et tous les autres fournisseurs de votre stack. Parlons runbooks de cache et chemins de reprise pour vos charges Next.js critiques.
Discuter de l'observabilité du cache