Un client vend des pièces détachées pour motoculteurs, environ 3 400 références réparties sur une douzaine de catégories. Chaque mise en production régénère les pages HTML statiques du cache FastCGI, et pendant les premières minutes, les visiteurs les plus rapides tombent sur des fiches produit qui mettent quatre secondes à s’afficher au lieu de 200 millisecondes. Le pic de charge coïncide presque toujours avec une campagne d’e-mailing ou une promotion, ce qui rend le problème visible et coûteux en conversions perdues.
La solution retenue n’est pas une purge suivie d’un simple curl sur la page d’accueil, mais un script WP-CLI qui parcourt méthodiquement le sitemap XML du site et déclenche une visite de chaque URL importante, avec un contrôle de parallélisme pour ne pas transformer le préchauffage en attaque par déni de service contre son propre serveur.
Pourquoi ne pas se contenter d’une purge manuelle
La purge manuelle après une mise à jour de contenu fonctionne bien pour un article isolé : on vide l’entrée de cache concernée et la prochaine visite la régénère. Le problème change de nature après un déploiement complet, où le cache entier (ou une portion large de celui-ci) est invalidé d’un coup. Sans préchauffage, ce sont les visiteurs réels — souvent ceux qui arrivent depuis une campagne payante coûteuse au clic — qui financent involontairement la régénération du cache.
Sur ce projet, l’équipe a mesuré qu’environ 12 % des sessions issues d’Ads dans les cinq minutes suivant un déploiement tombaient sur une page non cachée, avec un temps de chargement multiplié par vingt. Le taux de rebond sur ces sessions précises grimpait de façon nette par rapport à la moyenne du site.
Construire la liste d’URL à partir du sitemap plutôt qu’à la main
Une liste d’URL codée en dur dans un script bash vieillit mal : un nouveau produit ajouté n’est jamais préchauffé, une catégorie supprimée génère des 404 inutiles. Le sitemap XML généré par WordPress (nativement depuis WordPress 5.5, ou par une extension SEO) reste à jour automatiquement et constitue donc la source de vérité la plus fiable pour établir la liste des pages à visiter.

Le script commence par récupérer les sitemaps d’index, puis chaque sous-sitemap de produits et de catégories, avant d’en extraire les URL avec un parseur XML plutôt qu’une expression régulière fragile.
#!/usr/bin/env bash
set -euo pipefail
SITE_URL="https://boutique.example.com"
CONCURRENCY=6
LOGFILE="/var/log/warmup/$(date +%Y%m%d-%H%M%S).log"
mkdir -p "$(dirname "$LOGFILE")"
echo "Récupération des URL depuis le sitemap…" | tee -a "$LOGFILE"
urls=$(curl -s "${SITE_URL}/sitemap_index.xml" \
| xmllint --xpath "//*[local-name()='loc']/text()" - \
| grep -E "sitemap-(product|category)" \
| while read -r sub; do
curl -s "$sub" | xmllint --xpath "//*[local-name()='loc']/text()" -
done)
echo "$urls" | xargs -P "$CONCURRENCY" -I{} \
curl -s -o /dev/null -w "%{http_code} %{time_total}s {}\n" "{}" \
| tee -a "$LOGFILE"
echo "Préchauffage terminé, $(echo "$urls" | wc -l) URL visitées." | tee -a "$LOGFILE"
Choisir un niveau de parallélisme raisonnable
Le paramètre -P de xargs fixe le nombre de requêtes simultanées. Sur ce projet, hébergé sur un serveur avec huit workers PHP-FPM, monter au-delà de six requêtes en parallèle faisait chuter le temps de réponse des visiteurs réels pendant la fenêtre de préchauffage, ce qui revient à déplacer le problème plutôt qu’à le résoudre. La bonne valeur se détermine empiriquement, pool par pool, en surveillant Query Monitor ou les métriques PHP-FPM pendant un test en environnement de recette.
- Trop peu de parallélisme : le préchauffage dure plus longtemps que la fenêtre de trafic critique.
- Trop de parallélisme : les workers PHP-FPM saturent et pénalisent les visiteurs réels pendant le préchauffage.
- La bonne cible : rester sous 70 % de la capacité du pool PHP-FPM en pointe.
Brancher le script dans la pipeline de déploiement
Le script est appelé automatiquement à la toute fin du déploiement, une fois que la nouvelle version du code est en place et que le cache a été explicitement vidé (avec wp cache flush côté objet et une purge FastCGI côté Nginx). L’ordre compte : préchauffer avant la purge reviendrait à remplir le cache avec l’ancienne version du site.
deploy:
script:
- wp cache flush --path=/var/www/boutique
- ssh nginx-host "nginx -s reload"
- ./scripts/warmup-sitemap.sh
after_script:
- test -s /var/log/warmup/*.log
La dernière ligne du job vérifie qu’un fichier de log non vide a bien été produit, ce qui suffit à détecter un échec silencieux du script sans complexifier la pipeline avec des assertions plus fines.
Limites : ce que ce préchauffage ne couvre pas
Ce dispositif préchauffe le cache de page pour les visiteurs anonymes. Il ne traite pas le cas des pages personnalisées pour un utilisateur connecté (compte client, panier), qui ne sont de toute façon jamais mises en cache de la même manière, et il suppose que le sitemap reste raisonnable en taille : au-delà de quelques dizaines de milliers d’URL, il faudrait prioriser les pages à plus fort trafic plutôt que de tout parcourir.
Le préchauffage n’est utile que s’il termine avant l’arrivée du trafic réel. Un script trop prudent qui met vingt minutes à préchauffer 3 000 pages ne sert à rien si le pic de trafic arrive dans les cinq premières minutes après le déploiement.
En résumé
Un script WP-CLI et bash de quelques dizaines de lignes, branché en fin de pipeline de déploiement, suffit à éliminer l’essentiel du coût du cache froid pour une boutique de taille moyenne. L’essentiel du travail consiste à calibrer le parallélisme au pool PHP-FPM disponible et à garder le sitemap comme source unique de vérité pour la liste des URL, plutôt que d’entretenir une liste séparée qui finira par diverger du contenu réel du site.