Le WordPress d'aujourd'hui, décodé pour les développeurs

Headless & API

Un webhook dupliqué republie un contenu déjà supprimé côté front headless

Deux envois du même webhook, quelques secondes d'écart, suffisent à faire réapparaître un article supprimé sur un front découplé. Diagnostic et correctif.

Par Clément Hadrot • 5 juillet 2025 • 4 min de lecture • Aucun commentaire
Un webhook dupliqué republie un contenu déjà supprimé côté front headless

Deux requêtes identiques, à quatre secondes d’intervalle, sur le même endpoint de revalidation : c’est ce que révèlent les journaux du front lorsqu’un article vient d’être supprimé dans WordPress, puis réapparaît quelques minutes plus tard comme si de rien n’était. Le symptôme déroute au premier abord, car la suppression semble avoir fonctionné : l’article a bien disparu de l’administration, et pourtant sa page continue de s’afficher côté public.

L’explication tient dans la manière dont WordPress traite parfois un même événement en plusieurs passes internes, chacune capable de déclencher le webhook censé prévenir le front du changement.

Diagnostic : pourquoi deux envois pour un seul clic

Le hook before_delete_post et le hook deleted_post se déclenchent tous deux lors d’une suppression définitive, mais un contenu passé par la corbeille traverse en réalité trois étapes distinctes : la mise à la corbeille (wp_trash_post), puis la suppression définitive (delete_post), potentiellement déclenchée par une tâche planifiée qui vide la corbeille après le délai configuré. Si un webhook est accroché à la fois sur wp_trash_post et sur deleted_post, un seul geste de l’éditeur — supprimer un article — envoie deux notifications distinctes au front, à quelques secondes ou plusieurs jours d’écart selon la configuration.

add_action( 'wp_trash_post', 'notifier_front_suppression' );
add_action( 'deleted_post', 'notifier_front_suppression' );

function notifier_front_suppression( $post_id ) {
    wp_remote_post( 'https://front.example.com/api/revalidate', array(
        'blocking' => false,
        'body'     => array( 'post_id' => $post_id, 'action' => 'delete' ),
    ) );
}

Si le front traite le second webhook (celui de deleted_post) après avoir déjà retiré l’article de son cache suite au premier, sans vérifier que l’action déjà exécutée reste cohérente, une confusion peut réintroduire l’ancien contenu depuis un cache intermédiaire non invalidé au bon moment.

L'essentiel à retenir : Deux envois pour un seul événement ; Manque une clé de déduplication ; Correctif par un identifiant unique et un cache court

Le vrai coupable : l’absence de clé de déduplication

Le problème de fond n’est pas le double déclenchement en lui-même — deux hooks pour deux étapes distinctes d’une suppression sont un comportement normal de WordPress — mais l’absence, côté front, d’un mécanisme qui reconnaît qu’un même identifiant d’article a déjà été traité récemment. Sans cette protection, chaque webhook reçu déclenche une reconstruction complète de la page, y compris quand la seconde reconstruction contredit la première à cause d’un cache CDN encore chaud.

Le correctif : une clé de déduplication à courte durée de vie

La correction ne se fait pas côté WordPress, mais côté front, en ajoutant une protection simple avant de traiter chaque webhook reçu :

const dejaTraites = new Map();

app.post('/api/revalidate', (req, res) => {
  const cle = `${req.body.post_id}-${req.body.action}`;
  const maintenant = Date.now();

  if (dejaTraites.has(cle) && maintenant - dejaTraites.get(cle) < 10000) {
    return res.status(200).send('Ignoré, déjà traité');
  }

  dejaTraites.set(cle, maintenant);
  // traitement réel de la revalidation…
  res.status(200).send('OK');
});

Une fenêtre de dix secondes suffit largement à absorber les doublons provoqués par des hooks WordPress qui se chevauchent, sans risquer d’ignorer un événement légitime survenu plus tard. Sur un projet avec plusieurs instances de front en parallèle, cette déduplication doit reposer sur un stockage partagé (une base clé-valeur comme Redis) plutôt que sur une simple variable en mémoire, propre à chaque instance.

Prévention : ne garder qu’un seul hook déclencheur

Une fois le correctif en place côté front, il reste utile de simplifier la source du problème côté WordPress. Plutôt que d’accrocher un webhook sur plusieurs hooks proches dans le cycle de vie d’un contenu, un seul hook suffisamment tardif et générique limite le risque de doublons dès l’origine :

  • Préférer transition_post_status pour couvrir publication, dépublication et mise à jour en un seul point d’entrée.
  • Réserver un hook dédié à la suppression définitive uniquement, sans dupliquer sur la mise en corbeille.
  • Journaliser chaque webhook envoyé, avec un horodatage, pour repérer rapidement une nouvelle source de doublon si le problème réapparaît sous une autre forme.

En résumé

Un webhook qui semble « rejouer » un événement déjà traité n’indique presque jamais un bug du côté de WordPress, mais l’absence d’idempotence côté récepteur. Le cycle de vie d’un contenu supprimé comporte naturellement plusieurs étapes, chacune capable de déclencher sa propre notification ; c’est au front de reconnaître qu’un même identifiant, traité deux fois en quelques secondes, ne doit être exécuté qu’une seule fois.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi