# Un site headless affichait du contenu périmé : le CDN ignorait les webhooks

> Un CDN devant un front Next.js gardait en cache une page pourtant invalidée côté ISR. Les visiteurs ont vu un contenu obsolète pendant des heures avant que le problème ne soit compris.

- Auteur : Clément Hadrot
- Publié le : 2023-03-13
- Mis à jour le : 2023-03-13
- Catégorie : Headless &amp; API
- URL : https://wpmoderne.dev.wordpress-developpement.fr/headless/cdn-ignorait-webhooks-contenu-perime-isr-nextjs/

## L’essentiel

- L'ISR invalidait bien la page côté serveur Next.js
- Le CDN en amont continuait de servir sa propre copie
- Une purge explicite du CDN manquait dans le webhook

Un client du secteur associatif nous a signalé un mardi matin qu'un communiqué corrigé la veille au soir, à propos d'un changement de date d'événement, continuait d'afficher l'ancienne date sur son site pourtant équipé d'un système de revalidation automatique censé éviter exactement ce genre de problème. Les visiteurs qui consultaient la page voyaient un contenu obsolète depuis plusieurs heures, malgré une correction bien enregistrée côté WordPress.

## Le contexte technique

Le site fonctionnait avec Next.js en ISR (Incremental Static Regeneration), et un webhook WordPress déclenchait une revalidation ciblée à chaque publication ou modification de contenu, via le point de terminaison `/api/revalidate` exposé par le front. Un CDN commercial était placé en amont du serveur Next.js pour accélérer la diffusion des pages à l'échelle internationale, une configuration ajoutée quelques semaines plus tôt sans que l'équipe n'ait pleinement anticipé son interaction avec le mécanisme de revalidation.

## Ce que la revalidation faisait réellement

Le webhook fonctionnait exactement comme prévu côté Next.js :

```
// pages/api/revalidate.js
export default async function handler(req, res) {
  if (req.headers['x-webhook-secret'] !== process.env.WEBHOOK_SECRET) {
    return res.status(401).json({ message: 'Non autorisé' })
  }

  try {
    await res.revalidate(`/actualites/${req.body.slug}`)
    return res.json({ revalidated: true })
  } catch (err) {
    return res.status(500).send('Erreur de revalidation')
  }
}
```

Cette fonction avait bien été appelée, le journal du serveur Next.js confirmait un `revalidated: true` quelques secondes après la modification de l'article dans WordPress. Le serveur Next.js lui-même servait déjà la version corrigée depuis longtemps quand le client a signalé le problème.

> L'essentiel à retenir : L'ISR invalidait bien la page côté serveur Next.js ; Le CDN en amont continuait de servir sa propre copie ; Une purge explicite du CDN manquait dans le webhook

## Le vrai coupable : une couche de cache invisible depuis Next.js

Le CDN, configuré avec un temps de vie de cache de plusieurs heures pour réduire la charge sur l'origine, ne recevait aucune information sur la revalidation déclenchée côté Next.js. D'un point de vue du CDN, la page en question restait parfaitement valide selon sa propre durée de vie configurée, sans aucune raison de revenir interroger le serveur d'origine avant l'expiration naturelle du cache. Le mécanisme d'ISR avait fonctionné exactement comme prévu, à un niveau que le CDN, placé plus en amont, ne pouvait tout simplement pas voir.

## Le correctif : purger explicitement le CDN dans le webhook

La revalidation par webhook elle-même ne pose pas de problème et reste traitée en détail dans un autre article de ce blog ; ce qui manquait ici était une étape supplémentaire, ajoutant un appel à l'API de purge du CDN juste après la revalidation Next.js réussie :

```
export default async function handler(req, res) {
  if (req.headers['x-webhook-secret'] !== process.env.WEBHOOK_SECRET) {
    return res.status(401).json({ message: 'Non autorisé' })
  }

  const chemin = `/actualites/${req.body.slug}`

  await res.revalidate(chemin)

  // Étape ajoutée : purge explicite du CDN,
  // sans laquelle la revalidation Next.js reste invisible en amont.
  await fetch('https://api.cdn-exemple.com/purge', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.CDN_API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ paths: [chemin] }),
  })

  return res.json({ revalidated: true, purged: true })
}
```

## Vérifier que les deux couches restent bien synchronisées

Un test de contrôle a ensuite été mis en place : après chaque modification de contenu, un script vérifie que l'en-tête `Age` ou l'équivalent propre au CDN utilisé redescend bien à une valeur proche de zéro après l'appel de purge, confirmant que le CDN a effectivement recontacté l'origine plutôt que de continuer à servir sa copie précédente.

## Ce qui a changé dans notre méthode de mise en production

- Toute introduction d'un CDN devant un front Next.js en ISR déclenche désormais systématiquement une vérification explicite de l'interaction avec le mécanisme de revalidation existant, avant la mise en production.
- Le webhook de revalidation documente désormais clairement chaque couche de cache traversée, pour qu'un futur incident se diagnostique en quelques minutes plutôt qu'en plusieurs heures.
- Un contenu à caractère urgent (changement de date, alerte, correction sensible) fait l'objet d'une vérification manuelle immédiate après publication, en plus de la confiance accordée à l'automatisation.

> Chaque couche de cache ajoutée à une architecture headless est une couche de plus à informer explicitement d'un changement de contenu : aucune ne devine automatiquement ce que fait sa voisine.

## En résumé

L'ISR et le webhook fonctionnaient parfaitement, mais dans un système à plusieurs couches, un mécanisme d'invalidation qui ne connaît qu'une seule couche laisse toutes les autres livrées à elles-mêmes. Ajouter un CDN en amont d'un front headless n'est jamais neutre pour un système de cache déjà en place : chaque nouvelle couche doit être explicitement intégrée au parcours de revalidation, jamais supposée s'aligner d'elle-même.
