SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-23

Désactiver wp-cron et utiliser le cron système

WP-Cron dépend des visites et peut se bloquer ou s’accumuler. Désactivez-le, planifiez WP-CLI avec system cron et vérifiez l’exécution des tâches.

Ce qu’est wp-cron et pourquoi le cron système le remplace

WP-Cron est le planificateur de tâches intégré à WordPress. Il ne s’exécute que lorsqu’une personne demande une page. Rien dans WordPress ne le réveille de lui-même. À chaque requête qui n’est pas servie depuis un cache, WordPress lit la liste des tâches planifiées. Si l’une d’elles doit être exécutée, il envoie une seconde requête HTTP vers /wp-cron.php pour effectuer le travail. En transférant cette tâche au cron système, vous obtenez une exécution prévisible selon une fréquence fixe, que le site ait reçu mille visiteurs pendant cette minute ou aucun.

Deux lignes effectuent le travail : une constante dans wp-config.php et une entrée de crontab. Tout le reste de ce guide couvre ce que ces deux lignes n’indiquent pas. Vous devez notamment savoir avec quel utilisateur la tâche doit s’exécuter, comment vérifier que les événements planifiés ont bien été exécutés et quelles sont les trois façons dont cette configuration peut échouer sans rien afficher sur le site.

Les exemples utilisent /srv/www/example.com comme répertoire WordPress et www-data comme utilisateur du serveur web. Remplacez ces chemins et cet utilisateur par les vôtres partout où ils apparaissent.

Quel visiteur a déclenché les tâches cron et quel en est le coût sur un site très fréquenté

Chaque requête non mise en cache paie le coût de la vérification. WordPress charge l’option cron, compare les horodatages et, lorsqu’une tâche est due, appelle spawn_cron(), qui envoie une requête loopback non bloquante vers /wp-cron.php. Le visiteur n’attend pas le résultat. En revanche, un worker PHP l’attend. Sur un petit VPS exécutant PHP-FPM avec pm.max_children = 5, une tâche planifiée lente mobilise un cinquième de votre capacité PHP pendant toute sa durée. Elle a aussi de fortes chances d’être déclenchée pendant votre minute la plus chargée, car c’est à ce moment que le nombre de chargements de pages est le plus élevé.

WordPress limite les exécutions en double. Il prend un verrou dont la durée de vie est WP_CRON_LOCK_TIMEOUT, soit 60 secondes par défaut. Ainsi, plusieurs visiteurs simultanés ne lancent pas chacun une exécution. Le verrou limite les doublons. Il ne retire pas le traitement du chemin de la requête.

Mesurez la fréquence sur votre propre serveur avant de décider si elle est significative. Chaque requête loopback apparaît dans le journal d’accès du serveur web :

sudo grep -c 'wp-cron.php' /var/log/nginx/access.log

Apache écrit plutôt dans /var/log/apache2/access.log. Plusieurs milliers d’exécutions par jour représentent un coût réel. C’est le type de valeur que vous devez mesurer sur votre propre serveur, plutôt que de la reprendre d’un article, comme lorsque vous mesurez les performances d’un VPS avant et après toute autre modification.

La mise en cache change la situation. Si un cache de pages sert la plupart des requêtes sous forme de HTML statique, PHP ne s’exécute pas pour ces requêtes. La vérification cron n’a donc pas lieu. Un site très fréquenté et fortement mis en cache commence à se comporter comme le site peu fréquenté présenté ci-dessous.

Les visites déclenchent les tâches cron sur un site peu fréquenté

Sans visite, pas de cron. Un site qui reçoit quelques visites par jour exécute ses tâches planifiées quelques fois par jour, à des moments aléatoires correspondant à l’arrivée de ces visites.

Les symptômes sont toujours les mêmes. Un article planifié pour 09:00 reste marqué Missed schedule dans la liste des articles jusqu’à ce que quelqu’un charge une page. Les extensions de sauvegarde ne s’exécutent pas pendant la nuit. Les vérifications de mises à jour prennent du retard : le tableau de bord n’affiche aucune mise à jour alors qu’une mise à jour de sécurité est déjà disponible. Les e-mails de commande, les notifications de renouvellement et les avertissements d’expiration sont envoyés en retard.

Aucune erreur n’est journalisée. Du point de vue de WordPress, la tâche n’était pas en retard, car elle n’avait jamais été démarrée.

Étape 1 : désactiver le déclencheur des visiteurs dans wp-config.php

Ouvrez /srv/www/example.com/wp-config.php et ajoutez la constante suivante :

define( 'DISABLE_WP_CRON', true );

Placez-la au-dessus de la ligne /* That's all, stop editing! Happy publishing. */, car la ligne située juste sous ce commentaire nécessite wp-settings.php, et c’est à cet endroit que WordPress accroche la vérification du cron à init via wp-settings.php. Une constante définie après ce require est prise en compte trop tard pour modifier le comportement. Le fichier semble alors correct, mais le déclencheur continue de s’exécuter.

Vérifiez que la ligne se trouve bien à l’emplacement prévu :

grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.php

La constante n’empêche pas la planification des événements. Les extensions continuent d’ajouter des tâches à la file, exactement comme avant. Elle empêche uniquement les chargements de pages d’exécuter cette file. La file ne s’exécute donc plus du tout jusqu’à la fin de l’étape 3.

Elle n’empêche pas non plus les requêtes directes vers /wp-cron.php. N’importe qui peut toujours demander cette URL. Cela ne pose généralement pas de problème, car le fichier exécute uniquement les tâches arrivées à échéance. Bloquer cette URL dans la configuration de votre serveur web est facultatif. Si vous la bloquez, le mécanisme de repli curl situé vers la fin de ce guide ne fonctionnera plus non plus.

Étape 2 : installer WP-CLI

WP-CLI est l’outil officiel en ligne de commande de WordPress. Il nécessite le binaire PHP en ligne de commande, qui est fourni par un paquet distinct du module PHP du serveur web.

php -v
sudo apt install -y php-cli

Installez WP-CLI à partir de la version phar, comme le recommande le guide d’installation officiel :

cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --info

php wp-cli.phar --info affiche le chemin du binaire PHP, la version de PHP et la version de WP-CLI. Si les trois informations s’affichent, le fichier phar fonctionne. En août 2026, le guide d’installation indique PHP 7.2.24 comme version minimale, et Ubuntu 24.04 fournit PHP 8.3. Un serveur à jour dépasse donc largement cette exigence. Effectuez ensuite les mises à jour avec sudo wp cli update.

Exécutez WP-CLI avec l’utilisateur du site, jamais avec root :

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com core version

Avec root, WP-CLI refuse de démarrer :

Error: YIKES! It looks like you're running this as root.

Il suggère --allow-root. Ne l’utilisez pas ici. La raison est expliquée dans le premier cas d’échec ci-dessous.

Notez également que sudo -u www-data -i ne fonctionne pas, car le shell de connexion de ce compte est /usr/sbin/nologin et vous obtenez This account is currently not available.. Transmettre directement la commande à sudo -u contourne le shell de connexion, et la commande s’exécute donc correctement.

Vérifiez maintenant que WordPress voit bien la constante de l’étape 1 :

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com eval 'var_dump( DISABLE_WP_CRON );'

Cette commande affiche bool(true). Une erreur fatale indiquant qu’une constante n’est pas définie signifie que la ligne define() n’est pas atteinte. En général, elle se trouve sous la directive require.

Étape 3 : ajoutez l’entrée cron avec le bon utilisateur

Le bon utilisateur est celui qui possède les fichiers écrits par PHP. Vérifiez les deux éléments :

stat -c '%U %G' /srv/www/example.com/wp-content/uploads
grep -E '^(user|group) = ' /etc/php/8.3/fpm/pool.d/www.conf

Sur une installation Ubuntu par défaut, les deux commandes renvoient www-data. Si vous avez attribué au site un pool PHP-FPM dédié avec son propre utilisateur, ce qui est généralement le cas dans une configuration par site basée sur une stack LAMP sur Ubuntu 24.04, utilisez cet utilisateur pour tout ce qui suit.

Créez un répertoire de journaux dans lequel cet utilisateur peut écrire :

sudo install -d -o www-data -g www-data -m 750 /var/log/wp-cron

Modifiez la crontab de cet utilisateur :

sudo crontab -u www-data -e

Ajoutez une ligne :

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

Dans le détail. */5 l’exécute toutes les cinq minutes. flock -n prend un fichier de verrouillage et s’arrête immédiatement si une exécution précédente le détient encore. /usr/local/bin/wp est le chemin absolu dont cron a besoin. --path permet d’exécuter la commande depuis n’importe quel répertoire de travail. --due-now traite uniquement les événements dont l’heure est arrivée, au lieu de traiter tous les événements de la file d’attente. La redirection envoie la sortie standard et les erreurs vers un seul fichier que vous pouvez consulter.

En pratique, cette redirection est indispensable. Cron envoie la sortie d’une tâche par e-mail à son utilisateur, mais la plupart des images VPS n’intègrent aucun mail transfer agent. Cron journalise alors (CRON) info (No MTA installed, discarding output) et supprime la sortie. Un fichier conserve les éléments utiles au diagnostic.

Vérifiez que le fichier a bien été enregistré :

sudo crontab -u www-data -l

Pour plusieurs sites, utilisez une ligne par site et décalez les minutes afin qu’ils ne démarrent pas tous en même temps :

*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-one.lock /usr/local/bin/wp --path=/srv/www/one.example.com cron event run --due-now >> /var/log/wp-cron/one.log 2>&1
2-59/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-two.lock /usr/local/bin/wp --path=/srv/www/two.example.com cron event run --due-now >> /var/log/wp-cron/two.log 2>&1

Le journal grossit indéfiniment si vous ne le faites pas tourner. Écrivez /etc/logrotate.d/wp-cron :

/var/log/wp-cron/*.log {
    weekly
    rotate 4
    missingok
    notifempty
    compress
    create 640 www-data www-data
}

Vérifiez que la configuration est analysable sans rien modifier : sudo logrotate --debug /etc/logrotate.d/wp-cron.

Étape 4 : confirmer que les événements planifiés se sont bien exécutés

Une ligne de crontab enregistrée correctement ne prouve rien. Commencez par le contrôle le plus simple, puis poursuivez jusqu’à celui qui permet de conclure.

Tout d’abord, cron a-t-il lancé la commande ? Cron écrit dans le journal avec sa propre unité :

journalctl -u cron.service --since "15 min ago" | grep wp

Une entrée normale ressemble à ceci, après avoir retiré l’horodatage et le nom d’hôte au début :

CRON[24913]: (www-data) CMD (/usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1)

Cette ligne signifie que cron a lancé votre commande en tant que www-data. Elle n’indique pas si la commande a réussi.

Ensuite, WordPress a-t-il exécuté quelque chose ? Lisez le fichier journal :

sudo tail -n 20 /var/log/wp-cron/example.log

WP-CLI affiche une ligne par événement, puis un total :

Executed the cron event 'wp_version_check' in 0.418s.
Success: Executed a total of 2 cron events.

Les erreurs sont écrites dans le même fichier, ce qui est précisément l’objectif de 2>&1. La plupart des exécutions n’auront aucun événement arrivé à échéance et écriront très peu de contenu. Lisez donc le fichier après une exécution dont vous savez qu’elle avait des tâches en attente.

Enfin, vérifiez le fonctionnement de bout en bout. Planifiez un événement marqueur et observez sa disparition :

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event schedule wp_cli_cron_check now
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event list --fields=hook,next_run_relative --format=csv | grep wp_cli_cron_check

Attendez un intervalle, puis exécutez à nouveau la commande de liste. Le hook a disparu, car un événement ponctuel est retiré de la file lorsqu’il s’exécute. Aucun plugin n’enregistre de callback sur ce nom de hook. Son exécution n’a donc aucun autre effet sur le site. Si le hook est toujours listé après deux intervalles, la file n’est pas exécutée. Les deux premiers contrôles vous indiquent alors si le problème vient de cron ou de WP-CLI.

N’utilisez pas wp cron test dans ce cas. Cette commande vérifie si le déclenchement par un visiteur fonctionne, et elle renvoie une erreur lorsque DISABLE_WP_CRON est vrai. Sur un serveur correctement configuré, cette erreur est le résultat attendu et non un dysfonctionnement.

L’alternative avec un timer systemd

Si les tâches planifiées du reste du serveur utilisent déjà des services et timers systemd, ajoutez-y également WordPress. Chaque exécution apparaît alors dans systemctl list-timers, et la sortie est envoyée au journal au lieu d’être écrite dans un fichier que vous devez faire tourner.

Créez /etc/systemd/system/wp-cron-example.service :

[Unit]
Description=Run due WordPress cron events for example.com

[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

Puis /etc/systemd/system/wp-cron-example.timer :

[Unit]
Description=Run WordPress cron for example.com every 5 minutes

[Timer]
OnCalendar=*:0/5
AccuracySec=30s
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now wp-cron-example.timer
systemctl list-timers wp-cron-example.timer
journalctl -u wp-cron-example.service -n 20

systemd n’exécute pas simultanément deux instances du même service. Cette version n’a donc pas besoin de flock. Persistent=true lui permet de rattraper une exécution manquée pendant que la machine était arrêtée, ce qu’une entrée crontab ne peut pas faire.

Choisissez la crontab ou le timer. Les utiliser tous les deux pour le même site vide la file deux fois, et les exécutions en double d’une tâche d’e-mail ou de commande sont visibles par vos clients.

Pourquoi la tâche cron ne doit pas s’exécuter avec root

C’est le premier des trois problèmes possibles dans cette configuration. Si vous placez la tâche dans la crontab de root, WP-CLI s’arrête avant toute opération :

Error: YIKES! It looks like you're running this as root.

La file d’attente ne s’exécute jamais. Si vous n’avez pas redirigé la sortie, vous ne voyez pas le message. La correction dangereuse consiste à ajouter --allow-root, car tous les fichiers écrits par un plugin pendant cette exécution appartiennent alors à root. La requête web suivante s’exécute avec www-data et ne peut plus écrire dans ces répertoires. Le site commence alors à afficher des messages comme celui-ci :

Unable to create directory wp-content/uploads/2026/08. Is its parent directory writable by the server?

Rétablissez le propriétaire, puis déplacez la tâche :

sudo chown -R www-data:www-data /srv/www/example.com/wp-content

La crontab de root et celle de www-data sont des fichiers distincts. Supprimer la ligne dans l’une ne modifie donc pas l’autre. Vérifiez les deux :

sudo crontab -u root -l
sudo crontab -u www-data -l

Pourquoi cron signale wp: not found

Il s’agit du deuxième échec. Cron fournit aux tâches utilisateur un PATH très court, /usr/bin:/bin. WP-CLI est installé dans /usr/local/bin, qui ne figure pas dans cette liste. La tâche démarre, échoue en une fraction de seconde et le journal contient une seule ligne :

/bin/sh: 1: wp: not found

Consultez directement l’environnement de cron au lieu de faire des suppositions. Ajoutez temporairement cette ligne :

*/5 * * * * env > /tmp/cron-env.txt 2>&1

Consultez /tmp/cron-env.txt après un intervalle, puis supprimez la ligne. La valeur de PATH= dans ce fichier correspond exactement à l’environnement fourni à votre tâche.

Deux solutions sont possibles. Utilisez le chemin absolu /usr/local/bin/wp, comme à l’étape 3. Vous pouvez aussi définir le PATH une seule fois au début de la crontab, avant toutes les lignes de tâches :

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Le même problème se pose à un niveau inférieur. Le fichier phar wp commence par #!/usr/bin/env php. Le shell doit donc également pouvoir trouver php. Si PHP est installé en dehors de /usr/bin, ce qui arrive avec les compilations personnalisées et celles des panneaux de contrôle, vous obtenez :

/usr/bin/env: 'php': No such file or directory

Appelez explicitement l’interpréteur dans ce cas, par exemple /usr/local/bin/php /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now.

Pourquoi un intervalle d’une minute recrée le problème initial

Il s’agit de la troisième panne. * * * * * semble plus sûr que cinq minutes, mais sur un site chargé, vous revenez au problème initial. Si une exécution dure plus longtemps que l’intervalle, l’exécution suivante démarre alors que la première est encore en cours. Dix minutes plus tard, dix processus PHP sont actifs. Chacun utilise sa propre mémoire et sa propre connexion à la base de données.

Vérifiez directement l’accumulation :

ps -eo etimes,user,args | grep '[c]ron event run'

etimes correspond à l’âge du processus, en secondes. Une seule ligne est normale. Plusieurs lignes dont l’âge dépasse largement votre intervalle indiquent que les exécutions s’accumulent. Sur un petit VPS, cela finit par provoquer une erreur MySQL Too many connections, ou par l’arrêt forcé de PHP par le kernel pour récupérer de la mémoire. Vous pouvez le confirmer avec sudo dmesg -T | grep -i 'killed process'.

WP-CLI exécute directement les callbacks des événements au lieu de demander wp-cron.php. Le verrou de 60 secondes utilisé par WordPress contre les lancements en double ne s’applique donc pas ici. flock -n dans l’entrée de l’étape 3 empêche désormais les exécutions simultanées. Une exécution ignorée se termine immédiatement et silencieusement, comme prévu. Vous devez résoudre le problème des exécutions simultanées dans le crontab, et non compter sur le kernel de l’hôte pour le faire à votre place : même le placement des tâches tenant compte du cache ajouté dans le kernel Linux 7.2 détermine uniquement sur quel cœur un processus s’exécute, jamais combien de processus vous avez démarrés.

Choisissez l’intervalle à partir de la planification la plus courte dont vous dépendez réellement, puis mesurez d’abord la durée d’une exécution :

time sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

Cinq minutes constituent une valeur par défaut raisonnable : un article planifié à 09:00 est publié au plus tard à 09:05. Quinze minutes conviennent à un site qui ne contient aucun contenu soumis à une contrainte de temps. Une minute est réservée aux boutiques et aux extensions pilotées par une file d’attente qui en ont réellement besoin, et uniquement après avoir vérifié qu’une exécution se termine en quelques secondes.

Si vous ne pouvez pas installer WP-CLI

Certains hébergeurs bloquent les outils shell. Une simple requête HTTP vers wp-cron.php exécute la même file d’attente, mais en passant par toute la pile web :

*/5 * * * * /usr/bin/curl -sS --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron" > /dev/null

Ce que vous perdez concrètement :

  • L’exécution est limitée par les délais d’attente du serveur web et de PHP-FPM. Une tâche longue peut donc être interrompue en cours d’exécution.
  • Le certificat doit être valide, sinon curl s’arrête avec SSL certificate problem. Maintenez donc le renouvellement avec Certbot sur nginx.
  • Le cache des pages ne doit pas mettre wp-cron.php en cache. Sinon, les requêtes cron reçoivent une réponse mise en cache et rien ne s’exécute.
  • Vous n’obtenez aucune sortie par événement. La seule preuve qu’une tâche a été exécutée est donc l’effet produit.

-sS réduit la sortie de curl au silence en cas de succès, tout en affichant les erreurs. C’est le comportement souhaité dans une tâche cron.

Que faut-il ajouter à la planification du serveur

Une fois que cron gère la file d’attente de WordPress, regroupez au même endroit les autres tâches récurrentes du serveur. Vous pourrez ainsi les consulter plus facilement. Les correctifs de sécurité du système d’exploitation doivent être gérés par les mises à niveau automatiques, et non par une ligne cron que vous maintenez manuellement. Les mises à jour des extensions et des thèmes WordPress relèvent d’une autre décision : wp plugin update --all dans une crontab peut facilement interrompre un site en production à 3 h du matin sans personne pour surveiller la situation. Exécutez donc cette opération volontairement, ou après une étape de staging et une sauvegarde.

FAQ

La désactivation de WP-Cron empêche-t-elle la publication des articles programmés ?

Non, à condition qu’un autre mécanisme exécute la file d’attente. DISABLE_WP_CRON empêche uniquement le chargement des pages de déclencher la file d’attente. Les événements restent programmés exactement comme avant. Un article programmé pour 09:00 est publié lors de la première exécution de cron après 09:00. Avec un intervalle de cinq minutes, il est donc publié à 09:05 au plus tard. Si vous définissez la constante sans ajouter l’entrée cron, l’article reste dans la liste avec l’état Missed schedule jusqu’à ce qu’un mécanisme exécute la file d’attente.

Quel utilisateur doit exécuter la tâche cron de WordPress ?

Il doit s’agir de l’utilisateur propriétaire des fichiers dans lesquels PHP écrit. Il s’agit de www-data dans une installation Ubuntu par défaut. Vérifiez-le avec stat -c '%U %G' /srv/www/example.com/wp-content/uploads et comparez le résultat à la ligne user = de la configuration de votre pool PHP-FPM. Si vous exécutez la tâche en tant que root, WP-CLI s’arrête avec une erreur YIKES. Si vous forcez son exécution avec --allow-root, des fichiers appartenant à root sont créés dans wp-content, et le serveur web ne peut plus y écrire ensuite.

À quelle fréquence le cron système doit-il exécuter le cron de WordPress ?

Toutes les cinq minutes conviennent à la plupart des sites. Adaptez l’intervalle à la planification la plus courte dont vous dépendez réellement. Prévoyez aussi une marge suffisante par rapport à la durée d’une exécution, que vous pouvez mesurer en plaçant time devant la commande WP-CLI. Sur un site très sollicité, des intervalles d’une minute peuvent superposer les exécutions, sauf si flock les protège.

Pourquoi wp cron test échoue-t-il après la désactivation de WP-Cron ?

Parce que cette commande teste le déclenchement par les visiteurs. Elle signale une erreur lorsque DISABLE_WP_CRON est défini sur true. C’est le comportement attendu sur un serveur configuré ainsi. Vérifiez plutôt le chemin du cron système : consultez /var/log/wp-cron/example.log, ou programmez un événement marqueur avec wp cron event schedule et vérifiez qu’il a disparu de wp cron event list après l’exécution suivante.

Ai-je besoin de WP-CLI, ou curl vers wp-cron.php suffit-il ?

curl fonctionne et constitue la bonne solution si vous ne pouvez pas installer WP-CLI. Il est plus lent, car il charge WordPress via le serveur web, et il est limité par le délai d’expiration de la requête. WP-CLI exécute les événements dans un processus PHP en ligne de commande, sans délai d’expiration web. Il affiche une ligne par événement avec sa durée. Le journal indique donc exactement ce qui a été exécuté et combien de temps l’exécution a pris.