Vous programmez une sauvegarde toutes les nuits à 3 h du matin. Sauf que votre site reçoit trois visites par jour, toutes en journée. Résultat : la sauvegarde ne se déclenche jamais à 3 h, mais à 9 h 12, quand le premier visiteur arrive. Voilà tout le paradoxe du système de planification de WordPress. Il porte le nom de « cron », mais il ne fonctionne pas du tout comme le cron d’un serveur.
WP-Cron gère un tas de choses invisibles : publication des articles planifiés, vérification des mises à jour, envoi des mails, nettoyage de la base, tâches des plugins. Quand il déraille, ça se voit tout de suite. Dans cet article, je pars des bases pour ceux qui découvrent, puis je vais jusqu’aux réglages que les développeurs utilisent en production : cron serveur, intervalles personnalisés, planification de vos propres tâches et Action Scheduler.
L’essentiel à retenir ℹ️
WP-Cron n’est pas un vrai cron : il ne se réveille qu’au chargement d’une page. Sur un site peu visité, vos tâches partent en retard, d’où le fameux « Programmation manquée ». Sur un site très fréquenté, la vérification tourne pour rien à chaque visite. La solution tient en deux gestes : DISABLE_WP_CRON dans wp-config.php, puis une tâche cron serveur qui appelle wp-cron.php toutes les 15 minutes. Pour diagnostiquer, WP Crontrol ou wp cron event list. Pour vos propres tâches, testez wp_next_scheduled() avant de planifier. Pour les gros volumes, passez à Action Scheduler.
WP-Cron, c’est quoi au juste
WP-Cron est le planificateur de tâches intégré à WordPress. Son rôle : exécuter des actions à intervalles réguliers ou à une date précise. C’est lui qui publie votre article programmé pour lundi 8 h, qui vérifie chaque jour si une mise à jour de plugin est dispo, qui vide la corbeille au bout de 30 jours.
Le piège est dans le nom. Le vrai cron, celui d’un serveur Linux, est un service système qui tourne en permanence et déclenche des tâches à l’heure pile, que quelqu’un visite votre site ou non. WP-Cron, lui, ne tourne pas en continu. Il se réveille uniquement quand une page de votre site est chargée. À chaque visite, WordPress ouvre le fichier wp-cron.php en arrière-plan et demande : « est-ce qu’une tâche planifiée est en retard ? Si oui, exécute-la. »
Concrètement, WP-Cron ne connaît pas l’heure. Il connaît seulement le trafic. C’est un système « à la demande », et c’est de là que viennent tous ses problèmes.
Le double problème : trop peu de trafic ou trop
Cette dépendance au trafic crée deux situations pénibles, aux deux extrémités.
Sur un site à faible trafic, les tâches prennent du retard. Pas de visite entre minuit et 8 h ? Aucune tâche ne s’exécute pendant huit heures. Votre newsletter programmée part en retard, votre sauvegarde saute son créneau, vos articles planifiés ratent l’heure prévue. Le fameux message « Programmation manquée » sur un article, c’est presque toujours ça.
Sur un site à fort trafic, c’est l’inverse. À chaque chargement de page, WordPress vérifie s’il y a des tâches à lancer. Sur un site qui encaisse des milliers de visites à l’heure, cette vérification tourne des milliers de fois pour rien la plupart du temps. WP-Cron intègre bien un verrou (WP_CRON_LOCK_TIMEOUT, 60 secondes par défaut) pour éviter que deux processus se marchent dessus, mais la surcharge reste réelle. Si votre site rame et que vous cherchez d’où vient la fuite, WP-Cron fait partie des suspects à checker.
La bonne nouvelle : ces deux problèmes se règlent d’un coup, avec la même manipulation. On y vient.
Voir ce qui tourne : WP Crontrol et WP-CLI
Avant de toucher à quoi que ce soit, regardez ce qui se passe chez vous. WP-Cron est invisible par défaut, mais deux outils l’ouvrent en grand.

Le plus simple, côté interface, c’est le plugin gratuit WP Crontrol. Une fois installé, il ajoute un écran dans Outils → Événements Cron qui liste toutes les tâches planifiées, leur prochaine exécution, leur récurrence et le hook associé. Vous pouvez y lancer une tâche à la main, la modifier, la supprimer, ou même créer un nouvel événement sans écrire de code. C’est l’outil que je recommande à tout le monde pour un premier diagnostic.
Si vous avez accès au terminal, WP-CLI fait le même travail en plus rapide. Trois commandes à connaître :
# Lister tous les événements planifiés
wp cron event list
# Exécuter immédiatement toutes les tâches en attente
wp cron event run --due-now
# Vérifier que WP-Cron répond correctement
wp cron test
wp cron event list vous sort un tableau propre avec les hooks, les prochaines exécutions et les récurrences. C’est le point de départ de tout débogage sérieux. Si vous voyez des dizaines de tâches en double ou des hooks orphelins d’un plugin désinstallé, vous tenez peut-être la cause d’une lenteur.
Passer au vrai cron serveur (la manip qui règle tout)
Voilà le réglage qui fait passer du système bricolé au système fiable. L’idée : couper le WP-Cron déclenché par les visites, et le remplacer par une vraie tâche cron serveur qui appelle wp-cron.php à heure fixe.
Premier temps, désactivez le cron « à la demande ». Ouvrez wp-config.php (sauvegarde préalable, toujours) et ajoutez cette ligne avant la mention « That’s all, stop editing! » :
// Désactive le WP-Cron déclenché par les visites
define( 'DISABLE_WP_CRON', true );

Attention au contresens : cette constante ne désactive pas la planification. Vos tâches restent enregistrées, vos plugins continuent de programmer leurs événements. Vous coupez juste le déclencheur lié au trafic. Sans rien d’autre, plus aucune tâche ne s’exécuterait. Il faut donc un nouveau déclencheur.
Deuxième temps, créez la tâche cron serveur. Chez la plupart des hébergeurs mutualisés (o2switch, OVH, Hostinger…), ça se passe dans le cPanel, rubrique « Tâches Cron ». Vous y programmez une commande qui appelle votre wp-cron.php toutes les 15 minutes :
*/15 * * * * wget -q -O - >/dev/null 2>&1

Si vous avez WP-CLI sur le serveur, cette version est encore plus propre : aucune requête HTTP, et un vrai code d’erreur quand ça coince. Un simple php /home/user/public_html/wp-cron.php fonctionne aussi, mais sans le moindre retour sur ce qui s’est réellement exécuté.
*/15 * * * * /usr/local/bin/wp cron event run --due-now --path=/home/user/public_html >/dev/null 2>&1
Le */15 signifie « toutes les 15 minutes ». Pour un petit blog, une exécution toutes les 15 ou 30 minutes suffit largement. Sur une boutique WooCommerce avec des tâches sensibles au timing, descendez à 5 minutes. Résultat des courses : vos tâches partent à l’heure prévue, même à 3 h du matin sans visiteur, et WordPress arrête de vérifier le planning à chaque chargement de page. Les deux problèmes réglés d’un coup.
Vérifier que le cron serveur fait vraiment son travail
Une tâche cron mal écrite échoue en silence. Après la bascule, attendez le premier passage puis relancez wp cron event list : les prochaines exécutions doivent avoir avancé. Sans terminal, l’écran d’événements de WP Crontrol donne la même information, la colonne « prochaine exécution » se rafraîchit à chaque passage. Le test le plus parlant reste encore de programmer un article dans cinq minutes et de vérifier qu’il sort à l’heure.
Certains hébergements mutualisés d’entrée de gamme ne donnent pas la main sur les tâches cron. Un service externe comme cron-job.org ou EasyCron appelle alors l’URL wp-cron.php de votre site depuis l’extérieur, à la fréquence que vous choisissez. Le résultat est identique, à condition que l’URL reste publique et que DISABLE_WP_CRON soit bien à true.
Un dernier piège fait perdre des heures. WordPress enregistre les événements sous forme d’horodatages Unix, donc en UTC, pas dans le fuseau réglé dans Réglages → Général. WP Crontrol convertit l’affichage pour vous, mais si vous inspectez l’option cron à la main ou si vous passez un time() à vos fonctions de planification, vous manipulez de l’UTC. En été, deux heures d’écart avec l’heure française n’ont rien d’anormal.
Créer des intervalles sur mesure
WordPress ne connaît que quatre récurrences par défaut : hourly (toutes les heures), twicedaily (deux fois par jour), daily (une fois par jour) et weekly (une fois par semaine, ajoutée en WordPress 5.4). Si vous avez besoin d’autre chose, « toutes les 5 minutes » par exemple, il faut le déclarer vous-même.
Ça passe par le filtre cron_schedules. Ce snippet, à placer dans functions.php de votre thème enfant ou dans un plugin maison, ajoute un intervalle de cinq minutes :
// Ajoute un intervalle personnalisé « toutes les 5 minutes »
add_filter( 'cron_schedules', 'awp_intervalle_5min' );
function awp_intervalle_5min( $schedules ) {
$schedules['toutes_5_min'] = array(
'interval' => 300, // durée en secondes (5 × 60)
'display' => 'Toutes les 5 minutes',
);
return $schedules;
}
La clé interval est toujours en secondes. Une fois cet intervalle déclaré, il apparaît dans la liste déroulante de WP Crontrol et devient utilisable pour planifier vos propres tâches.
Planifier votre propre tâche
C’est là qu’on entre dans le développement. Programmer une tâche récurrente demande deux choses : enregistrer l’événement une seule fois, et accrocher une fonction au hook qui sera appelé à chaque exécution.
// 1. Planifier l'événement s'il n'existe pas déjà (ex. à l'activation du plugin)
if ( ! wp_next_scheduled( 'awp_nettoyage_quotidien' ) ) {
wp_schedule_event( time(), 'daily', 'awp_nettoyage_quotidien' );
}
// 2. Le code qui s'exécute à chaque déclenchement du hook
add_action( 'awp_nettoyage_quotidien', 'awp_faire_le_nettoyage' );
function awp_faire_le_nettoyage() {
// Votre logique : purge d'un cache, appel d'API, envoi d'un rapport...
}
Deux réflexes à ne jamais oublier. D’abord, testez avec wp_next_scheduled() avant de planifier, sinon vous empilez des doublons à chaque chargement de page (une des causes classiques de WP-Cron qui explose). Ensuite, pensez à nettoyer : à la désactivation de votre plugin, appelez wp_unschedule_event() ou wp_clear_scheduled_hook( 'awp_nettoyage_quotidien' ) pour ne pas laisser un hook fantôme derrière vous.
Une tâche qui ne s’exécute qu’une fois
Tout n’est pas récurrent. Pour déclencher une action à un moment précis puis l’oublier, relancer une synchronisation dans dix minutes, envoyer un mail de relance dans trois jours, c’est wp_schedule_single_event() qu’il vous faut. Même logique que wp_schedule_event(), sans le paramètre de récurrence.
// Déclenche le hook une seule fois, dans 10 minutes
wp_schedule_single_event( time() + 600, 'awp_relance_unique', array( 42 ) );
add_action( 'awp_relance_unique', 'awp_traiter_relance' );
function awp_traiter_relance( $post_id ) {
// $post_id vaut 42 : les arguments du tableau sont transmis au hook
}
Le troisième paramètre transmet vos arguments à la fonction, chaque valeur du tableau devenant un paramètre. Ces arguments font aussi partie de l’identité de l’événement, et WordPress s’en sert pour bloquer les doublons : planifier un événement à moins de dix minutes d’un événement identique (même hook, mêmes arguments) est purement ignoré. Pour obtenir deux tâches distinctes sur le même créneau, il faut leur passer des arguments différents.
Pour les grosses charges : Action Scheduler

Quand une tâche traite des milliers d’éléments (synchroniser un catalogue, envoyer 10 000 mails, réindexer un gros site), WP-Cron montre vite ses limites. Il n’est pas fait pour du traitement par lots à grande échelle, et il finit par timeouter.
La réponse s’appelle Action Scheduler. C’est une bibliothèque open source née chez Prospress pour les besoins de WooCommerce Subscriptions, aujourd’hui maintenue par Automattic. Elle stocke ses tâches dans des tables dédiées de la base plutôt que dans les options WordPress. Elle découpe le travail en lots, gère les réessais en cas d’échec, et offre un vrai journal consultable dans WooCommerce → État → Actions planifiées. Si vous faites tourner WooCommerce, vous l’avez déjà sans le savoir. Pour tout développement qui doit traiter du volume de façon fiable, c’est l’outil à adopter plutôt que de forcer WP-Cron à faire un métier qui n’est pas le sien.
Ce que je ferais à votre place
WP-Cron est un système malin pour un hébergement mutualisé sans configuration, mais il devient un problème dès que le timing compte. Trois réflexes à retenir :
- Diagnostiquez avant de bricoler. Installez WP Crontrol ou lancez
wp cron event listpour voir ce qui tourne vraiment. Vous y trouverez souvent des doublons ou des hooks morts. - Basculez sur un cron serveur dès que votre site a un enjeu de fiabilité : boutique, newsletter, sauvegardes. La combinaison
DISABLE_WP_CRONplus tâche cron toutes les 15 minutes règle d’un coup le retard des petits sites et la surcharge des gros. - Pour vos propres tâches, testez
wp_next_scheduled()avant de planifier, et pour les gros volumes, passez à Action Scheduler plutôt que de tordre WP-Cron.
Si je ne devais retenir qu’une chose : arrêtez de compter sur le trafic pour faire tourner vos tâches importantes. Cinq minutes dans le cPanel de votre hébergeur, et vous ne verrez plus jamais un « Programmation manquée ».
Pour aller plus loin
- WP Crontrol sur le répertoire officiel
- Documentation Action Scheduler
- WP-Cron dans le manuel du plugin (developer.wordpress.org)
Foire aux questions
Parce qu’aucun visiteur n’a chargé une page de votre site à l’heure prévue : WP-Cron ne s’est donc pas réveillé et l’article a raté son créneau. Le correctif durable consiste à désactiver le WP-Cron lié au trafic avec DISABLE_WP_CRON, puis à le remplacer par une tâche cron serveur qui tourne toutes les 15 minutes.
Non, à condition de mettre un cron serveur à la place. La constante DISABLE_WP_CRON coupe seulement le déclencheur lié aux visites, pas la planification elle-même : vos plugins continuent d’enregistrer leurs événements normalement. Sans nouveau déclencheur en revanche, plus aucune tâche ne s’exécute.
Toutes les 15 minutes couvrent le besoin d’un blog ou d’un site vitrine. Descendez à 5 minutes si vous gérez une boutique WooCommerce, une newsletter ou des tâches sensibles au timing. Inutile de descendre sous la valeur de WP_CRON_LOCK_TIMEOUT, fixée à 60 secondes par défaut, le verrou de WordPress bloquerait les appels.
Oui. Un service de cron externe comme cron-job.org ou EasyCron appelle l’URL wp-cron.php de votre site à la fréquence voulue, depuis ses propres serveurs. C’est la solution quand votre hébergement ne propose aucune interface de tâches planifiées.
WordPress enregistre les événements sous forme d’horodatages Unix, donc en UTC, et non dans le fuseau défini dans les réglages du site. WP Crontrol convertit l’affichage pour vous, mais un time() passé à wp_schedule_event ou wp_schedule_single_event reste de l’UTC.
WP-Cron stocke tous ses événements dans une seule option de la base et convient aux tâches légères. Action Scheduler utilise des tables dédiées, découpe le travail en lots et gère les réessais en cas d’échec. C’est le bon choix dès qu’une tâche doit traiter des milliers d’éléments.
WP Crontrol permet de supprimer un événement en un clic depuis l’écran Outils puis Événements Cron. En ligne de commande, la commande wp cron event delete suivie du nom du hook fait la même chose. Ces hooks orphelins viennent presque toujours de plugins désinstallés sans nettoyage.
