Aide en ligne
Tous les articles

Lancer une page web depuis la crontab

Une tâche planifiée est parfois écrite sous la forme d'une page web : certains frameworks et applications (WordPress, par exemple) ne s'initialisent correctement que dans un contexte web, avec leurs variables de serveur, leur configuration et leurs sessions. Plutôt que de recréer cet environnement en ligne de commande, on demande à la crontab d'appeler l'adresse de la page, exactement comme le ferait un navigateur.

Le but, dans cet article : exécuter la page https://www.monsite.com/monscript.php chaque nuit à minuit.

La commande avec curl ou wget

Deux outils font cet appel, installés sur la plupart des serveurs (which curl wget donne leur chemin) :

curl -fsS -m 300 -o /dev/null "https://www.monsite.com/monscript.php"

wget -q -O /dev/null --tries=1 --timeout=300 "https://www.monsite.com/monscript.php"
OptionRôle
curl -frenvoie un code d'erreur si le serveur répond par une erreur HTTP (404, 500…)
curl -s -Spas de barre de progression, mais les erreurs restent affichées
curl -m 300abandonne au bout de 300 secondes
wget -qmode silencieux
wget -O /dev/nulljette la page reçue au lieu de créer un fichier
wget --tries=1ne réessaie pas en cas d'échec (voir plus bas)
wget --timeout=300abandonne au bout de 300 secondes sans réponse

Attention à une erreur fréquente : wget -O -q. L'option -O attend un nom de fichier, donc -q est pris pour ce nom et la page est enregistrée dans un fichier appelé « -q ». Pour afficher la page au lieu de l'enregistrer, écrivez -q -O - (le tiret seul désigne la sortie standard).

Dans la crontab

Lancez l'édition de la crontab avec crontab -e, puis ajoutez la ligne, en remplaçant /usr/bin/curl par le chemin donné par which curl sur votre serveur :

0 0 * * * /usr/bin/curl -fsS -m 300 -o /dev/null "https://www.monsite.com/monscript.php"

Cette ligne appelle la page tous les jours à minuit. Pour choisir une autre fréquence, voir la page Crontab : exécutions planifiées sous Linux. Trois détails propres aux URL :

Pour garder une trace de la réponse de la page, redirigez-la vers un fichier au lieu de la jeter :

0 0 * * * /usr/bin/curl -fsS -m 300 "https://www.monsite.com/monscript.php" >> /var/log/monscript.log 2>&1

Éviter les mauvaises surprises

Les délais d'attente

Une page web n'a pas les mêmes limites qu'un script en ligne de commande. PHP arrête une page au bout de max_execution_time (30 secondes par défaut), et le serveur web coupe lui aussi les requêtes trop longues. Si le traitement est long, ajoutez en tête du script :

ignore_user_abort(true);  // continue même si l'appelant se déconnecte
set_time_limit(0);        // pas de limite de durée de PHP

Le délai propre au serveur web (Timeout d'Apache, fastcgi_read_timeout de nginx) reste à vérifier. Si le traitement dépasse quelques minutes, c'est un signe qu'il vaut mieux le lancer en ligne de commande.

Les réessais de wget

Par défaut, wget attend la réponse pendant 15 minutes et, en cas de coupure ou de délai dépassé, il réessaie jusqu'à 20 fois. La page peut donc être exécutée plusieurs fois par erreur. C'est pourquoi les exemples utilisent --tries=1 et --timeout. Avec curl, -m fixe la durée maximale et aucun réessai n'a lieu.

Les exécutions qui se chevauchent

Si la page peut durer plus longtemps que l'intervalle entre deux appels, empêchez le lancement d'une seconde copie avec flock :

*/5 * * * * /usr/bin/flock -n /tmp/monscript.lock /usr/bin/curl -fsS -m 240 -o /dev/null "https://www.monsite.com/monscript.php"

Qui peut appeler la page ?

Une page web est accessible à tout le monde : un visiteur, ou un robot d'indexation, peut la lancer en tapant son adresse. Pour une page qui modifie des données, envoie des e-mails ou consomme des ressources, c'est un risque. Protégez-la en vérifiant un jeton secret envoyé par la crontab dans un en-tête (et non dans l'adresse, qui apparaît dans les journaux du serveur et dans la liste des processus) :

/usr/bin/curl -fsS -m 300 -o /dev/null -H "X-Cron-Token: une-longue-valeur-secrete" "https://www.monsite.com/monscript.php"

Côté PHP, au début de monscript.php :

<?php
$attendu = 'une-longue-valeur-secrete';
$recu = isset($_SERVER['HTTP_X_CRON_TOKEN']) ? $_SERVER['HTTP_X_CRON_TOKEN'] : '';
if (!hash_equals($attendu, $recu)) {
    http_response_code(403);
    exit('Accès refusé');
}

Rangez la valeur secrète dans un fichier de configuration hors du dossier public, pas dans le script lui-même. Vous pouvez aussi limiter l'accès à la page à l'adresse du serveur (127.0.0.1) dans la configuration d'Apache ou de nginx, et demander aux robots de ne pas l'indexer.

Quand préférer la ligne de commande ?

Appeler une page web est pratique mais indirect. Dès que le script n'a pas vraiment besoin du contexte web, ou qu'il est long, exécutez-le directement avec PHP :

0 0 * * * /usr/bin/php /home/admin/www/monscript.php

Il n'y a alors ni limite de durée imposée par le serveur web, ni page accessible depuis Internet, ni jeton à gérer. Beaucoup de frameworks proposent d'ailleurs une commande dédiée (la console de Symfony ou Laravel, wp-cli pour WordPress).

Pour aller plus loin