Ce guide s’adresse aux rédacteurs dont les articles planifiés ne sortent jamais, et aux administrateurs qui doivent les débloquer. Vous programmez un article pour 9 h, vous revenez à midi, et la liste des articles affiche Publication manquée à la place de la date. WordPress n’a pas de planificateur à lui. Il ne cherche les articles dus qu’au moment où un visiteur charge une page qui exécute du PHP, alors tout ce qui empêche ces vérifications laisse votre article en attente. Vous allez ranger votre site dans l’une de quatre causes, publier les articles déjà bloqués, puis confier la planification à une vraie tâche cron pour que ça cesse.
Ce que vous allez accomplir
- Savoir en une commande si WordPress garde encore la tâche de publication de l’article bloqué, ou s’il l’a perdue.
- Ranger la cause dans l’un de quatre groupes en cinq minutes environ, avec trois commandes.
- Publier les articles qui ont déjà raté leur heure, sans retaper leurs dates.
- Remplacer le planificateur dépendant des visites par une tâche cron système, par cPanel ou par SSH.
- État final : un article test programmé deux minutes plus tard sort en moins de cinq minutes, sans qu’un visiteur soit nécessaire.
Prérequis
- WordPress 5.3 ou plus récent, et un compte avec le rôle Administrateur.
- Un accès shell avec l’utilisateur du site, par SSH (le protocole de terminal à distance chiffré) ou par le Terminal de cPanel. Les commandes supposent que le site se trouve dans
~/public_html. - WP-CLI, l’outil de ligne de commande de WordPress, disponible sous le nom
wp. Vérifiez avecwp --info. Si votre hébergeur ne l’offre pas, l’étape 7 donne un chemin cPanel qui ne demande aucun shell. - Un accès à cPanel → Advanced → Cron Jobs, ou le droit de modifier la crontab de l’utilisateur du site. La crontab est la liste des commandes planifiées propre à chaque utilisateur sur un serveur Linux.
- Une différence honnête entre hébergeurs : certaines plateformes infogérées lancent déjà leur propre appel de cron pour vous. Demandez-le à votre hébergeur avant d’en ajouter un deuxième. Deux appels ne brisent pas le site, mais ils rendent les journaux plus difficiles à lire.
Comment ça fonctionne
Publier un article planifié, c’est une chaîne de cinq maillons, et chacun peut céder sans bruit.
Quand vous cliquez sur Planifier, WordPress enregistre l’article avec le statut future et range une tâche unique nommée publish_future_post dans une option de base de données appelée cron. La pièce de WordPress qui exécute cette tâche s’appelle WP-Cron, un planificateur qui n’a aucune horloge à lui. À chaque chargement de page qui atteint vraiment PHP, WordPress lit la liste des tâches. Si l’une d’elles est due, il envoie une requête en boucle locale, c’est-à-dire une requête HTTP que le serveur adresse à sa propre adresse web publique, vers /wp-cron.php. Ce script exécute les tâches dues, et votre article passe de future à publish.
La liste des articles affiche Publication manquée pour tout article resté à future une fois sa date dépassée. Ces mots décrivent le symptôme, pas la cause. La cause, c’est l’un des maillons brisés ci-dessous.
Trois commandes vous disent quel maillon a cédé : wp cron test vérifie si le serveur peut s’appeler lui-même, wp cron event list montre si les tâches s’accumulent, et wp option get timezone_string montre si l’horloge du site s’accorde avec la vôtre. L’arbre ci-dessous transforme leurs réponses en correctif.
Étapes
1. Confirmez que l’article est vraiment bloqué. Lancez ceci depuis le dossier WordPress pour lister tous les articles qui attendent encore de sortir.
cd ~/public_html
wp post list --post_status=future --fields=ID,post_title,post_date
+------+-------------------------------+---------------------+
| ID | post_title | post_date |
+------+-------------------------------+---------------------+
| 4812 | Autumn maintenance window | 2026-09-16 09:00:00 |
| 4817 | New pricing page walkthrough | 2026-09-17 14:30:00 |
+------+-------------------------------+---------------------+
Toute post_date dans le passé signale un article manqué. Notez les identifiants. Un tableau vide veut dire que rien n’est bloqué en ce moment, et que vous regardez un problème qui s’est réglé tout seul, ce qui pointe vers le groupe B plus bas.
2. Demandez à WordPress s’il arrive à s’appeler lui-même. C’est le test de boucle locale, et il sépare le groupe A de tous les autres.
wp cron test
Success: WP-Cron spawning is working as expected.
Une ligne Error: à la place veut dire que la requête en boucle locale a échoué. C’est le groupe A, et l’étape 5 en fait le diagnostic. Attention : si cette commande passe, ça ne veut pas dire que le cron s’exécute assez souvent, seulement que l’appel réussirait si quelque chose le déclenchait.
3. Regardez la file d’attente. Elle montre chaque tâche que WordPress conserve et le moment où chacune devait s’exécuter.
wp cron event list --fields=hook,next_run_gmt,next_run_relative --due-now
+---------------------+---------------------+-------------------+
| hook | next_run_gmt | next_run_relative |
+---------------------+---------------------+-------------------+
| publish_future_post | 2026-09-16 13:00:00 | -2 days 1 hour |
| wp_version_check | 2026-09-16 14:11:02 | -2 days 0 hours |
+---------------------+---------------------+-------------------+
Un next_run_relative négatif veut dire que la tâche est en retard. Plusieurs tâches en retard sur des crochets sans lien entre eux, c’est le groupe B : rien ne déclenche la file. Une seule tâche publish_future_post en retard pendant que le reste est à jour, c’est le groupe D, parce que la file roule mais que cette tâche-là meurt chaque fois.
Si publish_future_post n’apparaît pas du tout, WordPress a perdu la tâche. Voyez le premier point du Dépannage.
4. Comparez l’horloge du site à la vôtre. Un mauvais fuseau horaire publie les articles des heures trop tôt ou trop tard, et peut faire paraître un article manqué dès l’enregistrement.
wp option get timezone_string
wp option get gmt_offset
wp eval 'echo wp_date( "Y-m-d H:i T" ), "\n";'
date -u '+%Y-%m-%d %H:%M UTC'
America/Toronto
0
2026-09-18 10:42 EDT
2026-09-18 14:42 UTC
La troisième ligne donne l’heure que WordPress croit qu’il est. Elle doit correspondre à l’horloge accrochée à votre mur. Si timezone_string est vide et que c’est gmt_offset qui porte la valeur, vous êtes dans le groupe C. Un décalage fixe ne suit pas l’heure avancée, alors les articles dérivent d’une heure deux fois par année. Réglez plutôt un fuseau nommé.
wp option update timezone_string 'America/Toronto'
wp option update gmt_offset 0
Success: Updated 'timezone_string' option.
Changer le fuseau ne déplace pas les articles déjà planifiés, parce que leur date est enregistrée en heure locale et en UTC. Ouvrez chaque article touché et réglez sa date de nouveau.
5. Testez la boucle locale depuis le serveur (groupe A). La requête qui compte part du serveur, alors faites le test là plutôt que depuis votre portable. Remplacez example.com par votre domaine.
curl -sS -o /dev/null -w 'status %{http_code} in %{time_total}s\n' -m 20 \
"https://example.com/wp-cron.php?doing_wp_cron=$(date +%s).0000"
status 200 in 0.412s
Un 200 veut dire que le chemin est libre. Tout autre code nomme l’obstacle :
- 403 ou 503 avec une page de défi : un pare-feu applicatif web ou un proxy bloque l’appel. Lisez les en-têtes avec
curl -sIet cherchezcf-mitigatedou une valeurServer:qui nomme le proxy. - 401 : le site est protégé par une authentification HTTP de base, chose courante sur les copies de préproduction. La boucle locale ne peut pas taper de mot de passe.
Could not resolve hostou une expiration de délai : le serveur n’arrive pas à joindre son propre nom public. Demandez à votre hébergeur si les requêtes sortantes vers l’adresse du site sont permises.- Une erreur de certificat : le serveur ne fait pas confiance à son propre certificat TLS, ce qui arrive avec les noms d’hôtes internes et les certificats autosignés.
Vous pouvez contourner chacun de ces cas en n’utilisant pas HTTP du tout. Les étapes 7 et 8 exécutent la file localement, alors la requête ne quitte jamais le serveur.
6. Publiez les articles déjà bloqués. Videz d’abord le verrou dont WordPress se sert pour éviter deux exécutions simultanées. Un verrou périmé, laissé par une exécution plantée ou par un cache d’objets défectueux, bloque toutes les exécutions suivantes tant qu’il reste là.
wp transient delete doing_cron
wp cache flush
Success: Transient deleted.
Success: The cache was flushed.
Exécutez maintenant toutes les tâches dues à la main. Ça publie les articles manqués avec leurs dates d’origine.
wp cron event run --due-now
Success: Executed a total of 6 cron events.
Confirmez avec la liste de l’étape 1.
wp post list --post_status=future --fields=ID,post_title,post_date
Les lignes en retard devraient avoir disparu. Si un identifiant est encore là avec une date passée, publiez-le directement avec wp post update 4812 --post_status=publish, qui conserve la date d’origine de l’article.
7. Passez à une vraie tâche cron dans cPanel. Cessez de faire dépendre WordPress des visiteurs. Ajoutez cette ligne à wp-config.php, au-dessus de la ligne qui dit /* That's all, stop editing! */.
define( 'DISABLE_WP_CRON', true );
Avec WP-CLI, vous pouvez la définir sans ouvrir le fichier.
wp config set DISABLE_WP_CRON true --raw
Success: Added the constant 'DISABLE_WP_CRON' to the 'wp-config.php' file with the raw value 'true'.
Ouvrez ensuite cPanel → Advanced → Cron Jobs. Sous Common Settings, choisissez Every 5 Minutes, ce qui remplit les champs d’horaire avec */5 * * * *. Mettez ceci dans la case Command, en remplaçant <user> par votre nom d’utilisateur cPanel.
/usr/local/bin/php /home/<user>/public_html/wp-cron.php >> /home/<user>/logs/wp-cron.log 2>&1
Utilisez la version de PHP que le compte roule vraiment, pas celle du système. Sur un serveur cPanel où le compte est réglé à PHP 8.2, le chemin est habituellement /opt/cpanel/ea-php82/root/usr/bin/php. Confirmez-le avant d’enregistrer.
which php
/opt/cpanel/ea-php82/root/usr/bin/php
Cinq minutes, c’est le bon intervalle pour un site qui publie des articles. Choisissez une minute si la même file envoie aussi des courriels de commande ou traite des paiements, parce que ces tâches valent les exécutions de plus. Créez d’abord le dossier de journaux s’il n’existe pas, avec mkdir -p ~/logs.
8. Ou pilotez-la avec WP-CLI par SSH. Ce chemin évite complètement HTTP, alors aucun pare-feu ni proxy ne peut y toucher. Ouvrez la crontab de l’utilisateur du site.
crontab -e
Ajoutez une seule ligne, avec votre propre chemin et le chemin complet de wp obtenu par which wp.
*/5 * * * * cd /home/<user>/public_html && /usr/local/bin/wp cron event run --due-now >> /home/<user>/logs/wp-cron.log 2>&1
Enregistrez, puis confirmez que l’entrée est bien là.
crontab -l | grep wp-cron
*/5 * * * * cd /home/<user>/public_html && /usr/local/bin/wp cron event run --due-now >> /home/<user>/logs/wp-cron.log 2>&1
Cron roule avec un chemin de recherche très court, alors écrivez les chemins complets pour wp comme pour le dossier. Le guide pour remplacer WP-Cron par le cron système approfondit le choix de l’intervalle et l’arbitrage entre PHP et WP-CLI.
Vérifier que ça fonctionne
Faites la preuve avec un article qui n’a aucune autre raison d’exister.
- Créez un article daté de deux minutes plus tard.
wp post create --post_title='Cron smoke test' --post_status=future \
--post_date="$(date -d '+2 minutes' '+%Y-%m-%d %H:%M:%S')" --porcelain
4821
- Attendez six minutes, puis relisez le statut. Utilisez l’identifiant que la commande a imprimé.
wp post get 4821 --field=post_status
publish
- Vérifiez que le journal du cron montre des exécutions récentes et aucune erreur.
tail -n 5 ~/logs/wp-cron.log
Un fichier vide est le bon résultat pour la ligne WP-CLI, parce qu’une exécution propre n’imprime rien. Des lignes qui parlent de command not found ou de No such file veulent dire qu’un chemin de l’entrée cron est erroné.
- Confirmez que la file est à jour, sans tâche en retard.
wp cron event list --due-now --format=count
0
- Supprimez l’article test.
wp post delete 4821 --force
Success: Deleted post 4821.
Cinq sur cinq veut dire que la publication planifiée roule maintenant à l’heure, que quelqu’un visite le site ou non.
Dépannage
L’article affiche « Publication manquée » mais n’a aucune tâche dans la file. Cherchez-la avec wp cron event list --fields=hook,next_run_relative | grep publish_future_post. Aucune sortie veut dire que la tâche a disparu. Ça arrive après une importation de base de données, avec une extension qui vide l’option cron, ou après un plantage en cours d’exécution. Reprogrammez-la en ouvrant l’article et en enregistrant une nouvelle date, ou publiez-le tout de suite avec wp post update <ID> --post_status=publish.
Les articles sortent, mais avec une heure de décalage. C’est l’heure avancée qui rencontre un décalage fixe. Réglez timezone_string sur un fuseau nommé comme à l’étape 4 et laissez gmt_offset à 0. Vérifiez le résultat avec wp eval 'echo wp_date( "Y-m-d H:i T" ), "\n";' en été et en hiver si vous voulez en avoir le cœur net.
wp cron test passe, et pourtant les articles ratent encore leur heure. La boucle locale fonctionne, mais rien ne la déclenche. C’est le cas le plus fréquent sur un site derrière un cache de pages complet, où les visiteurs reçoivent du HTML enregistré et où PHP roule rarement. Les étapes 7 et 8 règlent ça pour de bon. Si le site passe par Cloudflare, WP-Cron qui ne se déclenche pas derrière Cloudflare couvre les vérifications propres au proxy.
La file se vide, mais une tâche ne finit jamais. Une seule tâche plus longue que la limite de temps de PHP tue toute l’exécution avant que les suivantes aient leur tour. Trouvez le crochet lent avec wp cron event run --due-now --debug, qui imprime chaque crochet au moment où il démarre. Les boutiques avec un gros arriéré de tâches en arrière-plan ont souvent besoin de l’intervalle d’une minute de l’étape 7 plutôt que de cinq.
Tout marche depuis le shell, mais pas depuis le cron. Cron roule avec un environnement minimal et un dossier de travail différent. Ajoutez cd /home/<user>/public_html && au début de la commande, écrivez des chemins absolus pour php et wp, et lisez ~/logs/wp-cron.log après la prochaine exécution.
Quand ce n’est pas la bonne approche
Si toute l’administration est cassée ou que le site renvoie une erreur critique, la planification est un symptôme plutôt que le problème. Réglez d’abord l’erreur fatale, avec l’écran Santé du site dans l’administration WordPress et le journal d’erreurs PHP. Si vos tâches doivent démarrer à quelques secondes près de leur heure cible, un cron aux cinq minutes ne suffira pas. WordPress n’est pas le bon outil pour ce besoin. Confiez ce travail à un processus de traitement dédié, avec sa propre file. Si votre hébergeur lance déjà un appel de cron et interdit DISABLE_WP_CRON, suivez sa méthode documentée plutôt que d’ajouter un deuxième appel. Pour la liste complète des crochets et des fonctions de planification, consultez la documentation cron de WordPress, et pour chaque option utilisée plus haut, la référence de la commande 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.