add_action( 'save_post', 'ma_fonction' );
add_action( 'wp_after_insert_post', 'ma_fonction' );
Ces deux lignes déclenchent, en apparence, la même fonction au même moment : juste après l’enregistrement d’un article. Pourtant, un développeur qui a déjà tenté de lire une métadonnée ou un terme de taxonomie fraîchement associé depuis un hook save_post, sans succès, sait que ce n’est pas toujours vrai. La différence entre ces deux hooks tient au moment exact de leur déclenchement dans la séquence d’enregistrement, et elle peut expliquer des bugs qui ne se manifestent que dans certains contextes précis.
Ce que fait réellement save_post
Le hook save_post se déclenche à l’intérieur de la fonction wp_insert_post(), juste après l’écriture de la ligne principale dans la table wp_posts. À ce stade, les métadonnées personnalisées et les termes de taxonomie ne sont pas nécessairement tous écrits : sur l’écran d’édition classique, ce sont justement les fonctions accrochées à save_post elles-mêmes qui se chargent d’enregistrer les métadonnées des metaboxes, via update_post_meta(). Autrement dit, à l’intérieur d’un même hook save_post, l’ordre de priorité entre plusieurs fonctions accrochées détermine si une métadonnée écrite par l’une est déjà disponible pour l’autre.
Ce que garantit wp_after_insert_post

Introduit dans WordPress 5.6, sorti en décembre 2020, le hook wp_after_insert_post se déclenche après save_post, une fois que WordPress considère que l’insertion complète du post, y compris ses métadonnées et ses termes associés dans le contexte de l’appel en cours, est terminée. Sa signature transmet en plus un paramètre $update et l’objet $post_before, utile pour comparer l’état avant et après modification :
add_action( 'wp_after_insert_post', function( $post_id, $post, $update, $post_before ) {
$categorie = wp_get_post_terms( $post_id, 'category' );
if ( ! empty( $categorie ) ) {
// Fiable ici : la relation de taxonomie est posée.
error_log( 'Catégorie confirmée : ' . $categorie[0]->name );
}
}, 10, 4 );
Ce hook a été conçu en priorité pour l’API REST, où la création d’un article passe par plusieurs appels distincts : wp_insert_post() pour le contenu, puis des appels séparés pour les termes et les métadonnées via les champs enregistrés avec register_rest_field() ou register_post_meta(). Avant l’introduction de ce hook, il n’existait aucun point d’accroche fiable garantissant que toute cette séquence REST était terminée.
Un exemple concret de bug évité
Un cas typique : une extension qui envoie une notification à un service externe dès qu’un article est publié avec sa catégorie. Accrochée à save_post, cette notification peut partir avant que la catégorie ne soit associée, si la requête REST assigne les termes après l’appel initial de création. Le message envoyé contiendrait alors une catégorie vide, un comportement difficile à reproduire depuis l’écran d’édition classique, où l’ordre d’exécution diffère légèrement de celui de l’API REST.
Quand chacun reste pertinent
save_postreste adapté pour enregistrer soi-même des métadonnées personnalisées issues d’une metabox de l’écran d’édition classique : c’est son usage historique, et il continue de fonctionner correctement dans ce contexte précis.wp_after_insert_postdevient préférable dès qu’une action dépend de données annexes (termes, métadonnées enregistrées ailleurs) et doit fonctionner de façon identique, que l’article soit créé depuis l’éditeur classique ou via l’API REST.- Les variantes spécifiques par type de contenu,
save_post_{post_type}, existent aussi poursave_post, maiswp_after_insert_postne propose pas d’équivalent filtré par type : un test manuel sur$post->post_typereste nécessaire à l’intérieur du callback.
Piège à éviter : la double invocation lors d’une modification
Comme save_post, le hook wp_after_insert_post se déclenche aussi bien à la création qu’à la modification d’un article. Le paramètre $update, transmis en booléen, permet de distinguer les deux cas sans avoir à comparer manuellement $post->post_date et $post->post_modified :
add_action( 'wp_after_insert_post', function( $post_id, $post, $update ) {
if ( $update ) {
return; // On ne veut agir qu'à la création.
}
// Traitement réservé à la création initiale.
}, 10, 3 );
En résumé
Le choix entre ces deux hooks ne relève pas d’une préférence stylistique : save_post convient aux traitements internes à l’écran d’édition classique, tandis que wp_after_insert_post apporte une garantie de complétude nécessaire dès qu’une extension doit se comporter de façon identique entre l’éditeur classique et l’API REST. Ignorer cette distinction produit des bugs qui ne se manifestent que dans un seul des deux contextes, souvent longtemps après la mise en production initiale.