La documentation du Trac de WordPress décrit wp_after_insert_post ainsi : « Fires once a post, its terms and meta have been saved. » Cette phrase, en apparence anodine, résout un problème que beaucoup de développeurs rencontrent sans le nommer précisément : le hook save_post se déclenche avant que tout ne soit vraiment en place.
Concrètement, si vous accrochez une notification, un appel à une API externe ou une génération de PDF sur save_post, il n’est pas rare que les champs personnalisés associés à l’article ne soient pas encore enregistrés au moment où votre code s’exécute, ou que les termes de taxonomie ne soient pas encore assignés. Le résultat : des emails incomplets, des exports vides, des webhooks envoyés avec des données partielles.
Le problème concret avec save_post
save_post se déclenche à l’intérieur de wp_insert_post(), à un moment où le post est enregistré en base mais où certaines opérations liées (comme l’enregistrement des métadonnées via des blocs personnalisés, ou l’attribution de termes différée par d’autres extensions) peuvent encore être en cours. Ce hook se déclenche aussi plusieurs fois dans certains scénarios : une fois pour l’enregistrement de brouillon automatique, une fois pour chaque révision, une fois pour la publication finale. Sans précaution, un même traitement peut se répéter inutilement plusieurs fois de suite.
Ce que change wp_after_insert_post
Introduit dans WordPress 5.6, ce hook se déclenche une seule fois, à la toute fin de wp_insert_post(), après que les métadonnées ont été enregistrées et que les termes ont été assignés. Sa signature transmet quatre arguments : l’identifiant du post, l’objet WP_Post à jour, un booléen indiquant s’il s’agit d’une mise à jour, et l’objet WP_Post tel qu’il était avant la modification. Ce dernier paramètre est particulièrement utile pour comparer un ancien et un nouveau statut sans requête supplémentaire.

Mettre en place un déclenchement propre
Voici comment ne réagir que lorsqu’un article passe réellement au statut publié, en comparant l’état avant et après :
add_action( 'wp_after_insert_post', 'wpm_notifier_publication_finalisee', 10, 4 );
function wpm_notifier_publication_finalisee( $post_id, $post, $update, $post_before ) {
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
return;
}
$etait_publie = $post_before && 'publish' === $post_before->post_status;
$est_publie = 'publish' === $post->post_status;
if ( $est_publie && ! $etait_publie ) {
// Ici, les métadonnées et les termes sont déjà enregistrés.
$categories = wp_get_post_terms( $post_id, 'category', array( 'fields' => 'names' ) );
$auteur = get_the_author_meta( 'display_name', $post->post_author );
// Traitement fiable : email, appel API, écriture de log...
}
}
Ce filtre de garde en début de fonction évite les répétitions liées aux révisions automatiques, un piège fréquent avec save_post que ce nouveau hook ne résout pas automatiquement : il ne filtre pas les révisions à votre place, il faut le faire soi-même.
Pourquoi comparer post_before et post
Sans cette comparaison, chaque mise à jour ultérieure de l’article (correction de coquille, ajout d’un paragraphe) redéclencherait le même traitement puisque le statut resterait « publish » des deux côtés — mais la condition $est_publie && ! $etait_publie devient alors fausse, ce qui bloque bien les répétitions inutiles.
Variante : limiter à un type de contenu précis
Pour un site qui gère plusieurs types de contenus (articles, pages, produits), il est courant de vouloir ne réagir que sur un post type donné :
- Ajouter une vérification
if ( 'post' !== $post->post_type ) { return; }en tout début de fonction. - Utiliser
get_post_type( $post_id )si vous préférez ne pas dépendre de l’objet transmis. - Combiner avec
transition_post_statussi vous avez besoin de connaître l’ancien statut avant même que les métadonnées ne soient sauvegardées, au prix de perdre la garantie de complétude des données.
Règle maison : si votre traitement lit des champs personnalisés ou des termes de taxonomie, préférez toujours
wp_after_insert_postàsave_post. Le gain en fiabilité dépasse largement le coût d’apprentissage d’un hook de plus.
Ce que ce hook ne remplace pas
Il ne remplace pas transition_post_status lorsque vous avez besoin d’agir avant l’enregistrement définitif, ni publish_post qui reste pertinent pour des cas simples sans dépendance aux métadonnées. Il ne convient pas non plus si votre logique doit s’exécuter de façon synchrone avant que l’utilisateur ne voie la confirmation d’enregistrement dans l’éditeur : dans ce cas, un traitement asynchrone via une tâche planifiée reste préférable pour ne pas ralentir l’interface.
En résumé
Le hook wp_after_insert_post comble un vide réel entre save_post, trop précoce, et publish_post, trop spécifique. Il garantit que les métadonnées et les termes sont disponibles, à condition de filtrer soi-même les révisions et les autosaves, et de comparer l’état avant et après pour éviter les déclenchements en doublon. Une fois ce réflexe acquis, la fiabilité des traitements déclenchés à la publication s’en trouve nettement améliorée, sans plugin supplémentaire.