Aller au contenu

WP-Cron ou vrai cron : comment WordPress planifie ses tâches

Comprenez comment WP-Cron et un vrai cron système déclenchent chacun les tâches planifiées de WordPress, puis passez un site au cron système sans perdre ni doubler de tâches. Vous aurez la ligne crontab, les vérifications avant et après, et la marche à suivre pour revenir en arrière.

Les propriétaires de sites WordPress et les administrateurs de serveurs ont deux façons de déclencher les tâches planifiées : WP-Cron, le planificateur intégré à WordPress, ou le service cron du système d’exploitation. Vous verrez ci-dessous comment chacun démarre la même liste de tâches WordPress, et comment faire passer un site du premier au second sans risque. Vous êtes sans doute ici parce que quelque chose s’est exécuté en retard. Un article est marqué « Programmation manquée », un courriel de suivi WooCommerce part des heures après la commande, ou une extension de sauvegarde ne fait plus de rapport. Pour tout site dont vous dépendez, la réponse est un cron système qui lance WP-CLI chaque minute, plus DISABLE_WP_CRON dans wp-config.php. Gardez WP-Cron seulement là où vous n’avez ni accès shell ni accès cron.

Ce que vous allez accomplir

  • Savoir ce qui démarre réellement une tâche planifiée WordPress avec chaque méthode, et où chacune peut échouer.
  • Lire la file d’événements actuelle et repérer les tâches en retard avant de changer quoi que ce soit.
  • Installer une entrée cron qui s’exécute sous le bon utilisateur, ne peut pas se chevaucher et journalise ses erreurs.
  • Désactiver le déclenchement au chargement de page dans le bon ordre, pour qu’aucune tâche ne soit sautée ni exécutée deux fois.
  • Prouver que le nouveau réglage fonctionne avec un événement de test, et savoir l’annuler en une commande.

Prérequis

  • WordPress 6.x sur PHP 8.1 ou plus récent, sur un serveur Linux où vous pouvez modifier une crontab. Les mêmes principes valent pour les versions plus anciennes.
  • Un accès SSH, et WP-CLI (la commande wp, qui gère WordPress depuis un terminal) installé pour l’utilisateur propriétaire des fichiers du site. Vérifiez avec wp --info.
  • Le nom de cet utilisateur. Sur Debian et Ubuntu avec un serveur Web standard, c’est souvent www-data; avec les panneaux de contrôle et bien des VPS, chaque site a son propre compte.
  • Si le site tourne sous cPanel et que vous préférez son interface, la version cPanel de cette bascule couvre l’écran Tâches Cron étape par étape. Cet article reste en ligne de commande et fonctionne sur n’importe quel hôte Linux.

Comment ça fonctionne

Les deux méthodes travaillent sur la même file. WordPress enregistre chaque tâche planifiée, appelée événement, dans une seule option nommée cron de la table wp_options. Chaque entrée a un nom de crochet, une heure d’échéance et un intervalle de répétition facultatif. Les extensions ajoutent des événements avec wp_schedule_event(), et le cœur ajoute les siens, comme wp_version_check et wp_scheduled_delete. Ce qui change, c’est qui consulte cette file et décide qu’il est temps d’exécuter.

WP-Cron consulte la file pendant les chargements de page ordinaires. À chaque requête, WordPress cherche les événements dont l’échéance est passée. Depuis WordPress 6.9, cette vérification a lieu tout à la fin de la requête, une fois la page envoyée; les versions précédentes la faisaient près du début. S’il en trouve un, il envoie une requête rapide à son propre wp-cron.php; on parle de requête de bouclage, parce que le site s’appelle lui-même en HTTP. Il n’attend qu’une fraction de seconde, donc la page du visiteur n’est pas retardée. wp-cron.php exécute ensuite les événements dus dans un processus PHP distinct. Un verrou de courte durée, le transitoire doing_cron, empêche deux chargements de page de lancer le même lot en même temps. WordPress considère ce verrou comme périmé après 60 secondes, la valeur par défaut de WP_CRON_LOCK_TIMEOUT.

Cette conception a deux points faibles. Sans chargement de page hors cache, personne ne consulte la file. Un site tranquille peut passer des heures sans vérification, tout comme un site dont presque toutes les visites sont servies par un cache de pages ou un CDN. Et la requête de bouclage sort du serveur puis revient par le DNS, le pare-feu et tout mandataire, et chacun d’eux peut la bloquer sans laisser de trace dans WordPress.

Un cron système élimine ces deux faiblesses. Le démon cron du système d’exploitation lance une commande selon un horaire fixe, qu’il y ait des visites ou non. La commande est habituellement wp cron event run --due-now, qui charge WordPress dans un processus PHP en ligne de commande et exécute directement chaque événement en retard, sans aucune requête HTTP.

Diagramme comparant les deux déclencheurs de la file d'événements WordPress : WP-Cron a besoin d'un chargement de page hors cache et d'une requête de bouclage vers wp-cron.php qu'un pare-feu peut bloquer, alors qu'une crontab système lance l'exécuteur WP-CLI chaque minute

CritèreWP-Cron (par défaut)Cron système qui lance WP-CLICron système qui appelle wp-cron.php en HTTP
Ce qui le démarreUn chargement de page hors cacheL’horloge du serveurL’horloge du serveur
Fonctionne sur un site tranquille ou entièrement en cacheNonOuiOui
Dépend du DNS, du pare-feu et du mandataireOuiNonOui
Limites PHP qui s’appliquentLimites Web (délai, mémoire)Limites de la ligne de commande, en général sans délaiLimites Web
Ajoute du travail aux requêtes des visiteursUne vérification de la file à chaque pageAucunAucun
Exige un accès shell et crontabNonOuiAccès cron seulement

Les noms d’horaires de WordPress, comme hourly ou daily, sont des écarts minimaux, pas des rendez-vous. Un événement horaire s’exécute à la première vérification après son échéance. Avec WP-Cron, cette vérification dépend du trafic; avec un cron système, elle a lieu en moins d’une minute.

Étape par étape

1. Lisez la file avant de changer quoi que ce soit. Exécutez ces commandes en tant que propriétaire du site, depuis le répertoire WordPress :

wp cron event list --fields=hook,next_run_relative,recurrence
+------------------------------------+----------------------+------------+
| hook                               | next_run_relative    | recurrence |
+------------------------------------+----------------------+------------+
| action_scheduler_run_queue         | now                  | 1 minute   |
| wp_scheduled_delete                | now                  | 1 day      |
| wp_privacy_delete_old_export_files | 38 minutes 4 seconds | 1 hour     |
| wp_version_check                   | 5 hours 51 minutes   | 12 hours   |
| wp_update_plugins                  | 5 hours 51 minutes   | 12 hours   |
+------------------------------------+----------------------+------------+

Exemple de sortie. now signifie que l’événement est en retard et attend un déclencheur. Comptez-les pour avoir un chiffre à comparer plus tard :

wp cron event list --fields=hook,next_run_relative --format=csv | grep -c ',now$'

Un résultat de 0 à 2 est normal sur un site en santé. Des dizaines d’événements en retard veulent dire que le déclencheur actuel ne suit pas, ce qui confirme que la bascule en vaut la peine.

2. Vérifiez comment le site est déclenché aujourd’hui. La constante a peut-être déjà été définie par un ancien administrateur, sans rien pour la remplacer :

wp config get DISABLE_WP_CRON
wp cron test
Error: The constant or variable 'DISABLE_WP_CRON' is not defined in the 'wp-config.php' file.
Success: WP-Cron spawning is working as expected.

C’est la situation par défaut : WP-Cron est actif et sa requête de bouclage fonctionne. Si wp config get affiche 1 et que crontab -l ne montre aucune entrée WordPress, le site n’a actuellement aucun déclencheur, et l’étape 4 presse. Si wp cron test signale un code HTTP comme 403, la requête de bouclage est bloquée; derrière Cloudflare, le guide sur WP-Cron derrière Cloudflare explique pourquoi.

3. Lancez à la main la commande cron exacte, en tant que propriétaire du site. Cron démarre les commandes avec un PATH très court; utilisez donc le chemin complet de wp. Trouvez-le, puis lancez la commande comme cron le fera :

command -v wp
sudo -u www-data /usr/local/bin/wp --path=/var/www/example.com cron event run --due-now
/usr/local/bin/wp
Executed the cron event 'action_scheduler_run_queue' in 0.214s.
Executed the cron event 'wp_scheduled_delete' in 0.031s.
Success: Executed a total of 2 cron events.

Remplacez www-data et /var/www/example.com par votre utilisateur et votre chemin. S’il n’y a rien à exécuter, la dernière ligne indique Executed a total of 0 cron events, ce qui est normal. Ne l’exécutez pas en tant que root : WP-CLI refuse par défaut, et les fichiers écrits par une extension appartiendraient à root.

4. Installez l’entrée cron avec un verrou anti-chevauchement et un journal. Créez un répertoire de journaux où l’utilisateur du site peut écrire, puis ouvrez la crontab de cet utilisateur :

sudo install -d -o www-data -g www-data /var/log/wp-cron
sudo crontab -u www-data -e

Ajoutez une ligne :

* * * * * flock -n /tmp/wp-cron-example.lock /usr/local/bin/wp --path=/var/www/example.com cron event run --due-now --quiet >> /var/log/wp-cron/example.log 2>&1

flock -n garde un fichier verrou pendant l’exécution et saute le lancement suivant si le précédent est encore occupé; une tâche de sauvegarde lente ne peut donc pas empiler des copies d’elle-même. Le guide sur flock approfondit ce modèle. --quiet masque les lignes de succès, donc le journal ne grossit qu’en cas d’échec. Chaque minute convient à WooCommerce et aux sites qui utilisent Action Scheduler; toutes les cinq minutes (*/5) suffit pour un site vitrine. Confirmez l’enregistrement :

sudo crontab -u www-data -l | grep wp-cron

5. Regardez-la se déclencher avant de toucher à WordPress. Attendez deux minutes, puis consultez le registre du démon cron lui-même. La lecture du journal système demande habituellement sudo :

sudo journalctl -u cron --since "5 min ago" | grep www-data
Oct 29 09:21:01 web1 CRON[48213]: (www-data) CMD (flock -n /tmp/wp-cron-example.lock /usr/local/bin/wp --path=/var/www/example.com cron event run --due-now --quiet >> /var/log/wp-cron/example.log 2>&1)
Oct 29 09:22:01 web1 CRON[48266]: (www-data) CMD (flock -n /tmp/wp-cron-example.lock /usr/local/bin/wp --path=/var/www/example.com cron event run --due-now --quiet >> /var/log/wp-cron/example.log 2>&1)

Exemple de sortie. Sur les systèmes de la famille Red Hat, l’unité s’appelle crond et non cron. Le fichier /var/log/wp-cron/example.log devrait encore être vide. Les deux déclencheurs sont actifs pendant ces quelques minutes. Ce chevauchement présente peu de risque, parce que chaque exécuteur retire un événement de la file avant de l’exécuter, mais ne le laissez pas durer des jours.

6. Désactivez le déclenchement au chargement de page. Ajoutez la constante seulement maintenant :

wp config set DISABLE_WP_CRON true --raw
wp cron test
Success: Added the constant 'DISABLE_WP_CRON' to the 'wp-config.php' file with the raw value 'true'.
Error: The DISABLE_WP_CRON constant is set to true. WP-Cron spawning is disabled.

Cette erreur est le résultat voulu : les chargements de page ne lancent plus cron. wp-cron.php fonctionne toujours quand on l’appelle directement, ce qui compte si vous passez un jour à l’exécuteur HTTP. Pour revenir en arrière, lancez wp config delete DISABLE_WP_CRON et retirez la ligne de la crontab.

7. Couvrez chaque site d’un réseau multisite. Chaque site d’un réseau a sa propre file, et --due-now sans --url ne lit que le site principal. Remplacez la ligne de l’étape 4 par une boucle :

* * * * * flock -n /tmp/wp-cron-network.lock sh -c 'for u in $(/usr/local/bin/wp --path=/var/www/example.com site list --field=url); do /usr/local/bin/wp --path=/var/www/example.com --url="$u" cron event run --due-now --quiet; done' >> /var/log/wp-cron/network.log 2>&1

Sur un réseau de plusieurs centaines de sites, un passage peut durer plus d’une minute. Le verrou saute alors des lancements jusqu’à la fin du passage; réduisez plutôt la fréquence à toutes les cinq minutes au lieu de laisser le verrou décider. Les versions plus récentes de WP-CLI ajoutent une option --network qui fait la même boucle en une commande; si wp help cron event run la mentionne, wp cron event run --due-now --network peut remplacer la boucle for.

Validez que ça fonctionne

Planifiez un événement de test une minute plus tard, puis vérifiez qu’il a disparu après l’exécution suivante. Un événement sans code associé ne fait rien quand il s’exécute, sinon quitter la file :

wp cron event schedule sh_cron_probe '+1 minute'
Success: Scheduled event with hook 'sh_cron_probe' for 2026-10-29 13:31:12 GMT.

Attendez deux minutes sans charger aucune page du site, puis :

wp cron event list --fields=hook,next_run_relative | grep sh_cron_probe || echo "probe ran"
wp cron event list --fields=hook,next_run_relative --format=csv | grep -c ',now$'
probe ran
0

La sonde s’est exécutée sans aucun visiteur, et le nombre d’événements en retard de l’étape 1 est revenu entre 0 et 2. Vérifiez le fichier journal une fois de plus le lendemain : un fichier vide signifie une journée complète d’exécutions sans erreur.

Dépannage

Le journal indique wp: not found ou php: not found. Le PATH de cron n’inclut pas /usr/local/bin. Utilisez des chemins complets pour wp et, si WP-CLI ne trouve pas PHP, ajoutez PATH=/usr/local/bin:/usr/bin:/bin comme première ligne de la crontab.

Les événements s’exécutent, mais des fichiers ou des caches appartiennent à root. L’entrée se trouve dans la crontab de root avec --allow-root. Déplacez-la dans la crontab du propriétaire du site comme à l’étape 4, et corrigez la propriété des fichiers touchés avec chown.

La tâche d’une extension échoue sous WP-CLI alors qu’elle fonctionnait sous WP-Cron. PHP en ligne de commande lit son propre php.ini, donc les limites de mémoire et les extensions chargées peuvent différer. De plus, il n’y a pas de requête Web pour le code qui en attend une. Comparez wp --info avec le PHP du serveur Web. Si l’extension a besoin d’un contexte Web, passez à l’exécuteur HTTP : curl -sS -o /dev/null --max-time 300 "https://example.com/wp-cron.php?doing_wp_cron". Cet exécuteur dépend de nouveau du chemin de bouclage; confirmez d’abord que le serveur joint son propre site avec curl -I sur cette adresse.

Action Scheduler montre encore un arriéré. WooCommerce et bien des extensions font tourner leur propre file par-dessus WP-Cron, et traitent un lot chaque fois que l’événement action_scheduler_run_queue se déclenche. Un déclencheur plus rapide aide, mais les actions échouées ou bloquées demandent leur propre vérification; consultez l’audit de la file Action Scheduler.

Des tâches s’exécutent deux fois. Il existe deux déclencheurs système, souvent une entrée crontab plus le cron propre au panneau d’hébergement pour le même site, ou une entrée par utilisateur. Lancez sudo crontab -u <user> -l pour chaque utilisateur et gardez-en exactement une.

Quand ce n’est pas la bonne approche

Sur un hébergement mutualisé sans SSH ni accès cron, gardez WP-Cron. Si les tâches y attendent des heures, un service externe de surveillance de disponibilité peut demander la page d’accueil toutes les quelques minutes. Cela garde la file en mouvement, à condition que la page ne soit pas servie depuis le cache. Chez les hébergeurs qui font tourner chaque site dans un conteneur, utilisez le planificateur de la plateforme (un CronJob Kubernetes, par exemple) pour lancer la même commande WP-CLI au lieu d’une crontab dans le conteneur. Si vous gérez de nombreux comptes sur un même serveur, le guide sur la politique cron dans cPanel couvre les limites qui empêchent les tâches d’un compte de ralentir tous les autres. WordPress documente l’API des événements dans son manuel Cron, et chaque sous-commande utilisée ici figure dans la référence cron de WP-CLI.

Un WordPress qui rapporte vraiment mérite un opérateur — Stack Harbor opère cette pile sous WordPress Entreprise ; réservez une consultation d’infrastructure.