Aller au contenu principal
Site en cours de refonte — quelques pages peuvent bouger ou évoluer.
02 avril 2026

Next.js 16 sur Vercel : tu n’as “rien changé”… et ton cache a sauté

Sur Vercel, une upgrade Next.js peut changer le caching par défaut. Et quand ton contenu devient dynamique, ta facture suit.

10 min de lecture
52 vues
réactions
Partager :
Next.js 16 sur Vercel : tu n’as “rien changé”… et ton cache a sauté

Si tu héberges Next.js sur Vercel, il y a un piège assez vicieux : tu peux faire une upgrade “tranquille” (Next.js 15 → 16), ne toucher à aucun écran… et te retrouver avec des pages qui ne passent plus par le cache. Résultat très concret : plus de hits à l’origin, plus d’invocations de fonctions, plus de compute. Et la facture qui grimpe alors que, côté produit, rien n’a bougé.

Je pars d’un cas réel vu sur Vercel Community : l’upgrade a surtout révélé un changement de philosophie. On se retrouve avec une logique “cache implicite” qui devient “cache explicite”. Si tu ne vas pas re-déclarer ce qui doit être cacheable, tu subis.

Le vrai changement : du cache implicite à un cache explicite

Ce qui surprend, c’est rarement un bug “de perf” classique. C’est un changement de comportement par défaut. En gros, certaines routes qui étaient servies comme du contenu cacheable (ou suffisamment optimisées pour avoir très peu d’exécutions côté serveur) se mettent à être traitées comme dynamiques, donc recalculées plus souvent.

Et quand je dis “dynamiques”, ce n’est pas forcément que tes données changent tout le temps. C’est juste que Next/Vercel n’osent plus garantir que c’est safe à mettre en cache sans que tu le dises explicitement. Dans le thread, la piste remonte à l’apparition/nécessité d’une directive use cache (et plus largement à l’idée : “si tu veux du cache, marque-le”).

Ce genre de bascule arrive typiquement quand le framework durcit ses règles autour du rendu, des Server Components et des accès au contexte de requête (headers, cookies, auth, search params). C’est sain sur le papier. En prod, si tu n’audites pas, ça peut coûter cher.

Pourquoi ça double la facture sur Vercel (même si ton trafic n’a pas bougé)

Sur Vercel, la différence entre “servi depuis le cache/CDN” et “calculé dynamiquement” n’est pas qu’une question de TTFB. C’est une question de ressources facturées. Une page cache-hit, c’est souvent un fichier ou une réponse edge-served, et ça s’arrête là. Une page dynamique, c’est une exécution (Edge Function ou Serverless Function selon ton setup), plus des appels réseau (APIs, DB, services tiers), et parfois plus de logs/observability.

Le détail qui tue, c’est l’effet multiplicateur : une route qui passe de “souvent en HIT” à “souvent en MISS/BYPASS”, ce n’est pas +10% de coût. Ça peut être x2, x3, x10 selon ton trafic et le temps d’exécution. Et c’est exactement le genre d’écart que tu découvres trop tard, parce que l’app “marche”.

Comment prouver que c’est bien le cache (au lieu de te perdre dans des hypothèses)

Avant de corriger, il faut objectiver. Sinon tu vas patcher à l’aveugle, et tu ne sauras même pas si tu as vraiment réglé le problème.

Le signal le plus bête (et le plus utile) sur Vercel, c’est le header x-vercel-cache. Sur des pages qui étaient historiquement stables, tu veux voir du HIT (ou au pire du STALE maîtrisé). Si tu vois MISS en permanence, ou carrément BYPASS, tu tiens un morceau de la réponse.

curl -I https://ton-domaine.com/ta-page
# Regarde : x-vercel-cache, cache-control, age, et parfois x-nextjs-cache selon les cas

Ensuite, je corrèle avec deux métriques simples : le TTFB qui remonte (classique) et surtout le volume d’exécutions côté origin (invocations). Sur Vercel, tu peux souvent le voir via les dashboards/analytics/observability selon ton plan, ou au minimum via l’évolution des logs et des compteurs de fonctions. Quand tu as un trafic stable mais des invocations qui explosent, ce n’est pas “la perf”, c’est “la nature du rendu” qui a changé.

Dernier indice terrain : une page “cacheable” qui devient dynamique a souvent un profil de latence plus bruité. Les p50/p95 commencent à s’écarter, parce que tu dépends à nouveau du runtime, des cold starts, de la DB, d’un provider externe. Le cache, lui, a tendance à lisser.

Audit : retrouver les routes devenues dynamiques (celles qui te ruinent)

Je commence toujours par faire une liste mentale des routes qui devraient être stables. Pages marketing, docs, pages de listing peu volatiles, landing pages, articles… Si ces pages-là deviennent dynamiques, tu es en train de payer du compute pour afficher des pixels quasi identiques.

Ensuite, je cherche les déclencheurs classiques de “dynamic rendering” côté App Router. Tu n’as pas besoin d’avoir écrit “no-store” pour te faire piéger. Le simple fait d’accéder à certains signaux de requête peut suffire à invalider une stratégie statique, parce que la page devient dépendante du visiteur.

Typiquement, dès que tu lis des cookies pour de la personnalisation, que tu touches aux headers pour de l’A/B test, que tu utilises une auth côté serveur, ou que tu fais un rendu conditionné par la requête, tu as de grandes chances de sortir du monde “cache easy”. C’est parfois justifié. Souvent, c’est juste un détail (un composant qui lit un cookie “pour plus tard”) qui fait basculer toute la route.

À ce stade, je ne cherche pas la perfection, je cherche l’effet de levier : quelles routes à fort trafic ont perdu le cache, et pourquoi. Quand tu en corriges deux ou trois, tu récupères souvent l’essentiel du budget.

Reprendre le contrôle : rendre le cache explicite (use cache, revalidate, force-static)

Une fois que tu as identifié la route problématique, il faut choisir. Soit tu assumes que la page est réellement dynamique et tu optimises le runtime. Soit tu la remets dans un modèle cacheable, et tu isoles la partie dynamique.

Dans Next, ça se traduit souvent par un mélange de décisions explicites : un revalidate cohérent (ISR), un dynamic = 'force-static' si tu sais que tu peux, et/ou une stratégie de fetch cacheable. Et, selon les cas évoqués dans la discussion, l’usage de use cache pour réactiver une intention de cache là où tu pensais qu’elle était implicite.

// app/(marketing)/page.tsx
export const revalidate = 3600; // 1h : tu assumes un contenu "assez stable"
// export const dynamic = 'force-static'; // si tu sais que c'est safe

export default async function Page() {
  // Exemple : rendre ton intention explicite côté data
  const res = await fetch('https://api.exemple.com/content', {
    // selon ton cas : cache/revalidate côté fetch
    next: { revalidate: 3600 },
  });

  const data = await res.json();
  return <main>{data.title}</main>;
}

Mon conseil le plus “prod” ici : ne cherche pas à rendre toute la page dynamique “juste au cas où”. À l’inverse, ne force pas le statique si tu as de la personnalisation utilisateur. Le bon move est souvent de segmenter. Tu gardes la page cacheable, et tu déportes le bout réellement personnalisé dans un composant client qui appelle un endpoint, ou dans une route séparée qui, elle, a le droit d’être dynamique.

Ça évite l’erreur classique : “j’ai besoin d’un mini détail user-specific, donc je rends tout le HTML dynamiquement”. Sur Vercel, ce genre de confort se paye.

Les pièges fréquents : tu crois faire du statique, mais tu as mis un pied dans le dynamique

Le piège numéro un, c’est la dépendance implicite à la requête. Un cookie lu “pour tracker un variant”, un header exploité “pour la locale”, un accès à un objet qui dépend du visitor… et tu viens de rendre la route non déterministe. Next ne peut plus te garantir un cache global.

Le deuxième piège, c’est le “revalidate mal compris”. Mettre revalidate = 0 (ou une stratégie équivalente) parce que “je veux être à jour”, c’est souvent une décision de coût avant d’être une décision produit. Tu n’as pas forcément besoin d’être à jour à la seconde près pour 95% des pages.

Le troisième piège, c’est la fausse bonne idée de mettre du cache partout sans regarder la réalité métier. Si ton contenu change en continu, ton cache va juste être invalidé en boucle, tu vas payer le compute quand même, et tu vas en plus te créer des bugs de fraîcheur. Là, il faut optimiser autrement : perf backend, DB, edge compute bien dimensionné, et observability sérieuse.

Le protocole “pré-upgrade” que je veux voir si tu tiens à ton budget

Avant l’upgrade, je capture un baseline. Pas une moyenne vague. Je veux quelques routes critiques, leur TTFB, leur ratio de cache hit, et le volume d’invocations côté origin. Si tu ne sais pas ce que coûte une page “avant”, tu ne sauras pas prouver qu’elle coûte plus “après”.

Je passe l’upgrade en canary. Même 5% de trafic, même une fenêtre courte, ça suffit à voir une dérive de cache. Et surtout, ça permet un rollback sans drame. Upgrader Next en big bang sur la totalité du trafic, sur Vercel, c’est accepter de découvrir la vérité via la facture.

Je checke explicitement les headers de cache sur les routes à gros trafic. C’est bête, mais terriblement efficace. Tu fais quelques curl -I sur tes pages “stables”, et tu compares. Si tu vois que tu es passé de HIT à MISS/BYPASS, tu n’as pas besoin d’attendre 7 jours de billing pour savoir que tu as un problème.

Je liste les endroits où on lit cookies/headers et je décide si ça vaut vraiment un rendu serveur. Le rendu perso, c’est cool. Le rendu perso sur une landing à 80% identique pour tout le monde, c’est souvent une mauvaise affaire. Sépare, isole, et assume le coût seulement là où c’est justifié.

Mon avis : sur Vercel, “rendu dynamique par défaut” est un choix de produit… pas juste un détail technique

Je ne suis pas en train de dire “ne passez pas à Next.js 16”. Je dis juste qu’une upgrade de framework, aujourd’hui, peut déplacer une frontière économique. Et ça, beaucoup d’équipes ne le traitent pas comme un risque.

Si tu veux éviter l’upgrade piège, traite ton caching comme une API. Vérifie ce que le framework fait vraiment en prod. Rends tes intentions explicites. Et garde en tête la règle simple : sur Vercel, une page qui sort du cache n’est pas seulement plus lente. Elle est souvent plus chère, tous les jours, tant que tu ne corriges pas.

Sources

Cet article vous a plu ?

Commentaires

Laisser un commentaire

Entre 10 et 2000 caractères

Les commentaires sont modérés avant publication.

Aucun commentaire pour le moment.

Soyez le premier à donner votre avis !