Si vous avez déjà lancé une mise à jour de plugins sur un site WordPress 6.9.4 en production et vu apparaître une page blanche au pire moment, WP‑CLI devient vite plus qu’un “outil pratique”. C’est une façon reproductible d’exploiter WordPress côté serveur : backups, staging, migrations, déploiements, cache, cron… sans cliquer nulle part.
Le besoin / Le problème serveur
Le problème revient toujours sous la même forme : vous avez plusieurs sites, plusieurs environnements (prod/staging), plusieurs intervenants, et des opérations répétitives qui finissent faites “à la main”. Résultat : oublis (cache non vidé, permaliens non régénérés), erreurs humaines (mauvaise base, mauvais domaine), et interventions en urgence.
À la fin, vous saurez :
- Installer WP‑CLI proprement et l’exécuter avec le bon utilisateur système (sans permissions bancales).
- Standardiser vos commandes via
wp-cli.yml, des alias et des scripts idempotents. - Automatiser des sauvegardes “atomiques” (base + uploads) avec rétention.
- Cloner une prod vers un staging (rsync + DB +
search-replace) sans casser la sérialisation. - Orchestrer un déploiement (maintenance, update, migrations, cache, vérifs) en limitant les risques.
- Diagnostiquer quand WP‑CLI échoue (PHP, mémoire, DB, permissions, path, cache opcode).
Résumé rapide
- Verrouillez l’exécution : WP‑CLI doit tourner avec le même utilisateur que PHP-FPM/Apache, sinon bonjour les fichiers root et les permissions incohérentes.
- Centralisez la config : un
wp-cli.ymlet des alias évitent les “je me suis trompé de site”. - Sauvegardez en deux temps : dump DB + archive uploads, puis rotation (rétention) et vérification.
- Staging reproductible :
rsync+ import DB +search-replaceavec--all-tables-with-prefixet options adaptées. - Déploiement scripté : maintenance, update, cache flush, permaliens, healthcheck HTTP.
- Logs partout : journalisez chaque run (cron) et gardez un mode “dry-run” quand c’est possible.
Avant de commencer (prérequis)
Accès requis :
- SSH sur le serveur (ou conteneur) où tourne WordPress.
- WP‑CLI (binaire
wp) installé côté serveur. - Accès MySQL/MariaDB (au moins via les identifiants de
wp-config.php). - Accès au filesystem du site (typ.
/var/www/site).
Sauvegarde obligatoire :
- Avant toute automatisation, faites une sauvegarde manuelle testée (export DB + copie
wp-content/uploads). J’ai souvent vu des scripts “de backup” écraser des backups existants à cause d’un nom de fichier constant.
Versions :
- WordPress : 6.9.4 (avril 2026).
- PHP : 8.1+ (8.2/8.3 très courant en hébergement, adaptez les chemins).
- MySQL : 8+ ou MariaDB 10.6+ (les commandes restent identiques pour ce qu’on fait ici).
- Serveur web : Nginx ou Apache. Les exemples couvrent les deux.
Sources officielles utiles :
- WP‑CLI Commands (developer.wordpress.org)
- wp-config.php (developer.wordpress.org)
- Debugging in WordPress (wordpress.org)
- wp-cli/wp-cli (GitHub)
- php.ini directives (php.net)
Étape 1 : Installer et “verrouiller” WP‑CLI côté serveur
Le bug le plus coûteux que je vois avec WP‑CLI : exécuter wp en root “pour que ça marche”, ce qui crée des fichiers appartenant à root dans wp-content. Ensuite PHP-FPM (www-data) n’écrit plus, et vous découvrez le problème lors d’un upload ou d’une mise à jour.
Installer WP‑CLI (binaire) proprement
Sur Debian/Ubuntu, vous pouvez installer le binaire officiel. Exemple générique (à adapter à votre politique de sécurité) :
# Téléchargement du PHAR WP-CLI (source officielle)
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
# Vérification basique : WP-CLI répond
php wp-cli.phar --info
# Installation dans /usr/local/bin
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
# Vérification
wp --info
Exécuter WP‑CLI avec le bon utilisateur
Identifiez l’utilisateur qui possède les fichiers WordPress (souvent www-data, nginx, ou un user dédié type site1).
# Exemple : vérifier propriétaire du wp-config.php
ls -l /var/www/site/wp-config.php
# Exécuter WP-CLI avec le même user (évite les fichiers root)
sudo -u www-data -H wp --path=/var/www/site core version
Si vous êtes en environnement où sudo -u est interdit, créez un utilisateur de déploiement (ex: deploy) et donnez-lui les droits sur le répertoire du site. N’utilisez pas --allow-root sauf cas très contrôlé (CI/CD isolé, conteneur jetable).
Valider que WP‑CLI “voit” le bon WordPress
cd /var/www/site
# Vérifie que WP-CLI détecte WordPress et la DB
sudo -u www-data -H wp core version
sudo -u www-data -H wp option get siteurl
sudo -u www-data -H wp db check
Si wp db check échoue, ne passez pas à la suite. Corrigez d’abord : identifiants DB, host, droits, socket, ou variables d’environnement.
Étape 2 : Standardiser vos commandes avec wp-cli.yml et les alias
Quand vous automatisez, votre vrai ennemi est la variabilité : un chemin différent, un URL différent, un user différent. WP‑CLI sait se configurer par projet via wp-cli.yml. Faites-le, même si vous êtes seul.
Créer un wp-cli.yml par site
Placez-le à la racine WordPress (même niveau que wp-config.php), ou dans un répertoire parent si vous gérez plusieurs sites.
cd /var/www/site
nano wp-cli.yml
# Contenu de wp-cli.yml (YAML)
# Objectif : éviter de répéter --path et standardiser la sortie
path: /var/www/site
color: false
quiet: false
# Astuce : pour des scripts, vous pouvez forcer un format stable
# (ex. --format=json sur certaines commandes)
Test :
sudo -u www-data -H wp core version
Alias pour prod/staging (éviter les erreurs de cible)
WP‑CLI supporte des alias (fichier YAML dédié). Très pratique quand vous administrez prod et staging sur le même serveur ou via SSH.
Créez ~/.wp-cli/config.yml pour l’utilisateur qui lance les scripts (ex: deploy), ou ~/.wp-cli/aliases.yml selon votre organisation. Exemple simple avec alias locaux :
mkdir -p ~/.wp-cli
nano ~/.wp-cli/config.yml
# ~/.wp-cli/config.yml
@prod:
path: /var/www/site
user: www-data
@staging:
path: /var/www/site-staging
user: www-data
Utilisation :
# Exécute sur prod
sudo -u www-data -H wp @prod option get home
# Exécute sur staging
sudo -u www-data -H wp @staging option get home
Note : ici j’utilise quand même sudo -u www-data pour être cohérent. Vous pouvez aussi encapsuler ça dans des scripts shell.
Deux commandes que je standardise toujours
- Sorties parsables : privilégiez
--format=jsonquand disponible, ou--porcelainquand WP‑CLI le propose, pour éviter de parser du texte humain. - Mode “non interactif” : ajoutez
--yesaux commandes destructrices (mais uniquement dans des scripts qui journalisent et sauvegardent avant).
Étape 3 : Sauvegardes atomiques (DB + uploads) avec rétention
Une sauvegarde utile est une sauvegarde restaurable. Je privilégie un script qui génère (1) un dump DB, (2) une archive uploads, (3) un manifeste, puis (4) une rotation. Et je teste la présence des fichiers, pas juste le code retour.
Script de backup (copier-coller) avec rotation
Créez /usr/local/sbin/wp-backup.sh (ou dans un repo privé). Adaptez les chemins.
sudo nano /usr/local/sbin/wp-backup.sh
sudo chmod 750 /usr/local/sbin/wp-backup.sh
#!/usr/bin/env bash
set -euo pipefail
# --- Configuration (à adapter) ---
SITE_PATH="/var/www/site"
RUN_AS="www-data"
BACKUP_DIR="/var/backups/wordpress/site"
RETENTION_DAYS="14"
# --- Préparation ---
ts="$(date +%F_%H%M%S)"
mkdir -p "$BACKUP_DIR"
log() { echo "[$(date -Is)] $*"; }
log "Début backup: $SITE_PATH"
# 1) Dump DB (compressé)
DB_DUMP="$BACKUP_DIR/db_${ts}.sql.gz"
log "Dump DB vers $DB_DUMP"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" db export - | gzip -9 > "$DB_DUMP"
# 2) Archive uploads (sans cache)
UPLOADS_ARCHIVE="$BACKUP_DIR/uploads_${ts}.tar.gz"
log "Archive uploads vers $UPLOADS_ARCHIVE"
tar --exclude='*/cache/*' --exclude='*/wflogs/*' -C "$SITE_PATH/wp-content" -czf "$UPLOADS_ARCHIVE" uploads
# 3) Manifeste de backup (utile en restauration)
MANIFEST="$BACKUP_DIR/manifest_${ts}.txt"
log "Écriture manifeste $MANIFEST"
{
echo "DATE=$ts"
echo "SITE_PATH=$SITE_PATH"
echo "WORDPRESS_VERSION=$(sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" core version)"
echo "HOME=$(sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" option get home)"
echo "SITEURL=$(sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" option get siteurl)"
echo "DB_DUMP=$(basename "$DB_DUMP")"
echo "UPLOADS_ARCHIVE=$(basename "$UPLOADS_ARCHIVE")"
} > "$MANIFEST"
# 4) Vérifications minimales
log "Vérification présence fichiers"
test -s "$DB_DUMP"
test -s "$UPLOADS_ARCHIVE"
test -s "$MANIFEST"
# 5) Rotation
log "Rotation: suppression des backups > ${RETENTION_DAYS} jours"
find "$BACKUP_DIR" -type f -mtime +"$RETENTION_DAYS" -name '*.gz' -delete
find "$BACKUP_DIR" -type f -mtime +"$RETENTION_DAYS" -name 'manifest_*.txt' -delete
log "Backup terminé"
Lancer et journaliser
sudo /usr/local/sbin/wp-backup.sh | tee -a /var/log/wp-backup-site.log
Pour les sites avec gros médias, remplacez tar par rsync vers un stockage distant (S3, Borg, restic). WP‑CLI ne remplace pas une vraie stratégie 3-2-1, mais il rend la partie DB fiable et répétable.
Étape 4 : Créer un staging par clonage (rsync + export/import + search-replace)
Un staging “fidèle” doit copier le code, les uploads et la base, puis ajuster URLs. Le piège classique : faire un sed dans le dump SQL et casser la sérialisation. Utilisez wp search-replace, qui gère la sérialisation.
Cloner les fichiers avec rsync (sans caches)
# 1) Créer répertoire staging
sudo mkdir -p /var/www/site-staging
sudo chown -R www-data:www-data /var/www/site-staging
# 2) Synchroniser code + wp-content, en excluant les caches volumineux
sudo rsync -aHAX --delete
--exclude 'wp-content/cache/'
--exclude 'wp-content/wflogs/'
--exclude 'wp-content/uploads/cache/'
/var/www/site/ /var/www/site-staging/
Cloner la base (export/import) proprement
Ici je pars du principe que staging a sa propre base (recommandé). Créez DB + user côté MySQL au préalable.
# Export DB prod (stream) puis import staging
sudo -u www-data -H wp --path=/var/www/site db export -
| sudo -u www-data -H wp --path=/var/www/site-staging db import -
Si staging a un préfixe de tables différent, vous avez un problème plus complexe. En pratique, gardez le même $table_prefix sur staging pour simplifier.
Réécrire les URLs (sans casser la sérialisation)
Définissez vos URLs :
- Prod :
https://example.com - Staging :
https://staging.example.com
OLD="https://example.com"
NEW="https://staging.example.com"
# Remplace dans toutes les tables avec le préfixe WordPress
sudo -u www-data -H wp --path=/var/www/site-staging search-replace "$OLD" "$NEW"
--all-tables-with-prefix
--precise
--recurse-objects
--skip-columns=guid
# Ajuste home/siteurl explicitement (j’évite de compter sur le search-replace)
sudo -u www-data -H wp --path=/var/www/site-staging option update home "$NEW"
sudo -u www-data -H wp --path=/var/www/site-staging option update siteurl "$NEW"
Pourquoi --skip-columns=guid ? Le GUID n’est pas une URL “fonctionnelle”, c’est un identifiant. Le changer peut créer des effets de bord sur des flux ou des imports. J’ai déjà vu des plugins de syndication se comporter bizarrement après remplacement du GUID.
Désactiver les mails sortants sur staging (pratique, pas “joli”)
Le plus sûr : bloquer SMTP au niveau réseau. Mais côté WordPress, vous pouvez neutraliser l’envoi via un petit mu-plugin. C’est du code, donc versionnable, et ça évite le staging qui spamme vos clients.
sudo -u www-data -H mkdir -p /var/www/site-staging/wp-content/mu-plugins
sudo -u www-data -H nano /var/www/site-staging/wp-content/mu-plugins/disable-mail.php
<?php
/**
* Plugin Name: Staging - Désactivation des emails sortants
* Description: Empêche wp_mail() d'envoyer des emails depuis l'environnement de staging.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_filter( 'pre_wp_mail', function( $null, $atts ) {
// Retourne true pour simuler un envoi réussi.
// Risque : certains plugins attendent des erreurs. Adaptez si besoin.
return true;
}, 10, 2 );
Si un plugin a besoin d’emails en staging, remplacez ce filtre par une redirection vers une adresse unique, ou activez-le seulement sur un domaine staging via wp_get_environment_type().
Étape 5 : Déploiement “sans douleur” (maintenance, cache, migrations)
Le déploiement “à la main” échoue souvent sur deux points : l’ordre des opérations et le cache. Avec des page builders (Divi 5, Elementor, Avada), vous avez en plus des caches internes (CSS/JS générés) qui doivent être régénérés.
Script de déploiement : mise à jour core + plugins + thèmes + caches
Créez /usr/local/sbin/wp-deploy.sh. Ce script est volontairement conservateur : il active le mode maintenance, met à jour, exécute les upgrades DB si nécessaire, puis invalide des caches.
sudo nano /usr/local/sbin/wp-deploy.sh
sudo chmod 750 /usr/local/sbin/wp-deploy.sh
#!/usr/bin/env bash
set -euo pipefail
SITE_PATH="/var/www/site"
RUN_AS="www-data"
log() { echo "[$(date -Is)] $*"; }
log "Déploiement: $SITE_PATH"
# Vérification rapide : WordPress accessible via WP-CLI
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" core is-installed >/dev/null
# Mode maintenance
log "Activation maintenance"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" maintenance-mode activate
# Mises à jour
log "Mise à jour core (mineures + majeures selon politique)"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" core update
log "Mise à jour DB si nécessaire"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" core update-db
log "Mise à jour plugins"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" plugin update --all
log "Mise à jour thèmes"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" theme update --all
# Régénérations utiles
log "Flush des permaliens (évite les 404 après modifs rewrite)"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" rewrite flush --hard
# Caches WordPress
log "Purge cache objet/transients"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" cache flush || true
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" transient delete --all || true
# Page builders : on déclenche des actions côté WP si disponibles
# (Ces commandes peuvent ne pas exister selon versions/plugins ; on les protège.)
log "Tentative: purge Elementor (si installé)"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" elementor flush-css || true
log "Tentative: rebuild Avada/Fusion (si commandes WP-CLI exposées)"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" fusion clear-cache || true
log "Tentative: purge caches Divi (souvent via action admin, pas toujours en CLI)"
# Pas de commande standard WP-CLI pour Divi : on s'appuie sur cache flush/transients.
true
# Désactivation maintenance
log "Désactivation maintenance"
sudo -u "$RUN_AS" -H wp --path="$SITE_PATH" maintenance-mode deactivate
log "Déploiement terminé"
Remarque honnête : les commandes wp elementor flush-css / wp fusion clear-cache dépendent des plugins et de leur exposition WP‑CLI. Je les mets en “best effort” avec || true. Le socle fiable reste : rewrite flush, cache flush, transients, et éventuellement la purge cache serveur (FastCGI/Varnish) via curl si vous l’avez.
Déploiement + healthcheck HTTP
Ajoutez un test HTTP simple après déploiement. Ça attrape vite les erreurs PHP fatales ou les 502.
# Exemple healthcheck
URL="https://example.com/"
curl -fsS -o /dev/null -w "HTTP=%{http_code} TTFB=%{time_starttransfer}n" "$URL"
Étape 6 : Cron, logs et automatisation fiable
WP‑Cron dépend du trafic. Sur des blogs à faible audience, les tâches “planifiées” dérivent. Je préfère souvent désactiver WP‑Cron et déclencher wp cron event run via cron système.
Désactiver WP‑Cron (option serveur)
Dans wp-config.php :
<?php
// ... votre configuration existante
// Désactive WP-Cron (déclenché via trafic HTTP)
define( 'DISABLE_WP_CRON', true );
Créer un cron système (toutes les 5 minutes)
sudo crontab -u www-data -e
*/5 * * * * /usr/local/bin/wp --path=/var/www/site cron event run --due-now --quiet >> /var/log/wp-cron-site.log 2>&1
Sur des sites lourds, je passe plutôt par cron event run --due-now que cron event run --all. Ça limite les exécutions longues et les overlaps.
Automatiser backups + déploiements (cron) avec verrou (lock)
Le problème caché : deux backups qui se chevauchent (cron + run manuel) et vous obtenez des archives corrompues. Ajoutez un lockfile.
sudo nano /usr/local/sbin/wp-backup-locked.sh
sudo chmod 750 /usr/local/sbin/wp-backup-locked.sh
#!/usr/bin/env bash
set -euo pipefail
LOCK="/var/lock/wp-backup-site.lock"
# Crée un lock exclusif ; si déjà locké, on sort
exec 9>"$LOCK"
if ! flock -n 9; then
echo "[$(date -Is)] Backup déjà en cours, sortie."
exit 0
fi
/usr/local/sbin/wp-backup.sh
Planifiez :
sudo crontab -e
15 2 * * * /usr/local/sbin/wp-backup-locked.sh >> /var/log/wp-backup-site.log 2>&1
Fichiers de configuration complets
Ces fichiers sont des bases solides pour un serveur WordPress “classique” en 2026. Adaptez les chemins, domaines, sockets, et politiques de cache. Les exemples sont volontairement complets pour être copiés/collés.
Exemple nginx.conf (server block) + PHP-FPM
# /etc/nginx/sites-available/site.conf
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
root /var/www/site;
index index.php;
# SSL (à gérer via votre stack : certbot, acme, etc.)
ssl_certificate /etc/ssl/certs/example.com.fullchain.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
# Headers de base (à ajuster)
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# WordPress
location / {
try_files $uri $uri/ /index.php?$args;
}
# PHP
location ~ .php$ {
include snippets/fastcgi-php.conf;
# Adaptez à votre version PHP (8.1/8.2/8.3)
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 120s;
}
# Bloquer l'accès à des fichiers sensibles
location ~* /(wp-config.php|readme.html|license.txt) {
deny all;
}
# Cache agressif pour assets (attention aux builders qui régénèrent CSS)
location ~* .(css|js|jpg|jpeg|png|gif|webp|svg|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
try_files $uri =404;
}
}
Exemple .htaccess (Apache) minimal et sûr
# /var/www/site/.htaccess
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
# Protéger des fichiers sensibles
<FilesMatch "^(wp-config.php|readme.html|license.txt)$">
Require all denied
</FilesMatch>
# Headers (si mod_headers disponible)
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
Exemple wp-config.php (extraits pertinents serveur/WP‑CLI)
<?php
/**
* wp-config.php - extraits recommandés pour WordPress 6.9.4+ / PHP 8.1+
* Objectif : environnement propre pour WP-CLI, staging, debug maîtrisé.
*/
define( 'DB_NAME', 'wpdb_prod' );
define( 'DB_USER', 'wpdb_user' );
define( 'DB_PASSWORD', 'mot-de-passe-fort' );
define( 'DB_HOST', '127.0.0.1' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );
// Clés salts : générez-les via WordPress.org
// https://api.wordpress.org/secret-key/1.1/salt/
define( 'AUTH_KEY', '...' );
define( 'SECURE_AUTH_KEY', '...' );
define( 'LOGGED_IN_KEY', '...' );
define( 'NONCE_KEY', '...' );
define( 'AUTH_SALT', '...' );
define( 'SECURE_AUTH_SALT', '...' );
define( 'LOGGED_IN_SALT', '...' );
define( 'NONCE_SALT', '...' );
$table_prefix = 'wp_';
// Debug : à activer temporairement, jamais en continu sur prod
define( 'WP_DEBUG', false );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
// Cron piloté par le serveur (optionnel)
define( 'DISABLE_WP_CRON', true );
// Limite mémoire PHP côté WP (ne remplace pas php.ini)
define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );
// Forcer HTTPS derrière un reverse proxy (si applicable)
// if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) {
// $_SERVER['HTTPS'] = 'on';
// }
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
require_once ABSPATH . 'wp-settings.php';
Exemple php.ini (ou conf.d) orienté WordPress/WP‑CLI
Selon votre distro, ce sera plutôt un fichier dans /etc/php/8.2/fpm/conf.d/ ou /etc/php/8.2/cli/conf.d/. Le point clé : WP‑CLI utilise PHP CLI, donc sa config peut différer de PHP-FPM.
; /etc/php/8.2/cli/conf.d/99-wordpress.ini
memory_limit = 512M
max_execution_time = 120
date.timezone = Europe/Paris
; Logs d'erreurs (à adapter)
log_errors = On
error_log = /var/log/php/php-cli-wordpress-error.log
Vérification
Objectif : valider que vos automatisations font ce que vous pensez, et que WP‑CLI pointe au bon endroit.
Vérifier WP‑CLI, PHP et le contexte
wp --info
php -v
php -i | grep -E 'memory_limit|Loaded Configuration File'
Vérifier WordPress 6.9.4, DB, et URLs
sudo -u www-data -H wp --path=/var/www/site core version
sudo -u www-data -H wp --path=/var/www/site db check
sudo -u www-data -H wp --path=/var/www/site option get home
sudo -u www-data -H wp --path=/var/www/site option get siteurl
Tester le backup et la restauration “à blanc”
# Lancer un backup
sudo /usr/local/sbin/wp-backup.sh
# Vérifier les fichiers générés
ls -lh /var/backups/wordpress/site | tail -n 10
# Vérifier que le dump SQL est lisible
gzip -t /var/backups/wordpress/site/db_*.sql.gz
Healthcheck HTTP après déploiement
curl -I https://example.com/ | head -n 5
curl -fsS https://example.com/wp-json/ -o /dev/null -w "HTTP=%{http_code}n"
Si ça ne marche pas
Quand WP‑CLI échoue, la cause est rarement “WordPress”. C’est presque toujours : mauvais path, mauvais user, mauvaise config PHP CLI, ou DB inaccessible.
Diagnostic rapide (checklist)
- Mauvais répertoire : vous n’êtes pas dans le bon WordPress, ou
--pathpointe ailleurs. - Permissions : des fichiers appartiennent à root, ou
www-datan’a pas accès en écriture. - PHP CLI différent : WP‑CLI utilise
/usr/bin/php(CLI), pas PHP-FPM. - DB host/socket :
localhostpeut utiliser un socket,127.0.0.1TCP. Selon la conf MySQL, ça change tout. - Memory limit : imports DB et search-replace sur gros sites peuvent exploser la mémoire en CLI.
Commandes de diagnostic serveur utiles
# 1) Confirmer l'utilisateur effectif
whoami
id
# 2) Vérifier droits sur wp-content
namei -l /var/www/site/wp-content
ls -ld /var/www/site/wp-content /var/www/site/wp-content/uploads
# 3) Vérifier logs PHP CLI
tail -n 200 /var/log/php/php-cli-wordpress-error.log || true
# 4) Vérifier logs serveur web si les symptômes sont côté HTTP
sudo tail -n 200 /var/log/nginx/error.log || true
sudo tail -n 200 /var/log/apache2/error.log || true
# 5) Tester MySQL avec les identifiants (si possible)
mysql -h 127.0.0.1 -u wpdb_user -p -e "SHOW DATABASES;"
Tableau de diagnostic (symptômes réels)
| Symptôme | Cause probable | Vérification | Solution |
|---|---|---|---|
Error: This does not seem to be a WordPress installation. |
Mauvais --path ou exécution hors du répertoire WP |
ls -la et présence de wp-load.php |
Utiliser --path=/chemin ou créer wp-cli.yml |
PHP Fatal error: Allowed memory size ... exhausted |
Memory limit trop bas en PHP CLI | php -i | grep memory_limit |
Augmenter memory_limit côté CLI (conf.d) ou exécuter par lots |
Fichiers créés en root:root dans wp-content |
WP‑CLI exécuté en root | find wp-content -user root |
Arrêter --allow-root, corriger ownership, exécuter en www-data |
Error establishing a database connection via WP‑CLI |
DB_HOST/socket, firewall, identifiants | wp db check, test mysql |
Corriger DB_HOST, droits MySQL, réseau |
| Après migration, pages OK mais médias en 404 | Uploads non copiés ou mauvais chemin | ls wp-content/uploads, logs Nginx |
Rsync/tar des uploads, vérifier permissions et root Nginx/Apache |
Pièges et erreurs courantes
| Erreur | Cause | Solution |
|---|---|---|
Vous lancez un search-replace sur prod “pour tester” |
Absence de staging + pas de sauvegarde | Clone staging, test, puis prod avec backup et fenêtre de maintenance |
| Vous copiez un snippet WP‑CLI trouvé en ligne (2019) et ça casse | Options obsolètes, hypothèses fausses (PHP 7.x, vieux WordPress) | Valider sur WordPress 6.9.4, lire la doc des commandes |
Vous oubliez --skip-columns=guid et vous remplacez des GUID |
Confusion GUID vs URL | Refaire un import propre puis search-replace avec skip GUID |
| Permaliens cassés après migration | Rewrite rules non régénérées | wp rewrite flush --hard (et vérifier Nginx/Apache) |
| CSS/JS page builder “pas à jour” après déploiement | Cache builder + cache serveur | Purger transients/cache objet + purge builder si possible + purge FastCGI/Varnish |
| WP‑CLI marche en SSH, mais cron échoue | PATH différent, pas le même user, environnement minimal | Utiliser chemins absolus (/usr/local/bin/wp), rediriger logs, vérifier whoami dans le script |
| Erreur PHP “Call to undefined function” dans un script custom | Vous exécutez du PHP sans charger WordPress | Passer par wp eval-file ou charger wp-load.php explicitement |
Sécurité serveur
Automatiser = amplifier. Une commande mal ciblée devient un incident. Mes garde-fous côté serveur :
- Pas de WP‑CLI en root sur un serveur partagé. Si vous devez le faire en CI, isolez (conteneur jetable) et nettoyez les artefacts.
- Clés SSH dédiées pour les automatisations (cron/CI), avec command restrictions si possible.
- Backups chiffrés si vous les sortez du serveur (restic/borg), et rétention courte sur le serveur web.
- Moins de surface : bloquez l’accès web à
readme.html,wp-config.php, etc. (voir conf Nginx/Apache plus haut). - Journalisation : conservez
/var/log/wp-backup-site.loget/var/log/wp-deploy-site.logavec rotation logrotate. - Secrets : évitez d’écrire des mots de passe DB en clair dans vos scripts. Préférez lire depuis
wp-config.phpvia WP‑CLI, ou utilisez des variables d’environnement côté système (selon votre stack).
Si vous exposez des endpoints de purge cache (Varnish/FastCGI), protégez-les (ACL IP, token, mTLS). Une purge ouverte, c’est une attaque de perf à bas coût.
Ressources
- Référence des commandes WP‑CLI (developer.wordpress.org)
- Dépôt WP‑CLI (GitHub)
- Référence wp-config.php (developer.wordpress.org)
- Debug WordPress (wordpress.org)
- WordPress Core Trac (core.trac.wordpress.org)
- PHP en ligne de commande (php.net)
FAQ
WP‑CLI est-il compatible avec WordPress 6.9.4 ?
Oui, WP‑CLI reste l’outil standard côté serveur. Le point à surveiller n’est pas tant WordPress que PHP CLI (version, extensions, mémoire) et l’utilisateur système utilisé.
Pourquoi WP‑CLI échoue alors que le site fonctionne en navigateur ?
Parce que le navigateur passe par PHP-FPM/Apache, alors que WP‑CLI passe par php en CLI. Les php.ini peuvent être différents (mémoire, extensions, chemins), et les droits filesystem aussi.
Puis-je automatiser Elementor/Divi/Avada uniquement avec WP‑CLI ?
Partiellement. WordPress standard (cache, transients, permaliens) est automatisable. Les builders exposent parfois des commandes WP‑CLI, parfois non. Dans ce cas, basez-vous sur des purges génériques (cache objet, transients) et sur la purge cache serveur, puis faites un healthcheck.
Quelle est la commande la plus dangereuse en prod ?
wp search-replace sans sauvegarde, et wp db import sur la mauvaise base. Protégez-vous avec des alias (@prod/@staging) et un script qui refuse d’agir si l’URL ne correspond pas à l’environnement attendu.
Comment éviter de casser la sérialisation lors d’une migration ?
N’éditez pas le SQL au sed. Importez puis utilisez wp search-replace avec --recurse-objects et --precise. C’est plus lent, mais fiable sur des données sérialisées.
Dois-je vider les permaliens après chaque déploiement ?
Non. Mais après une migration, un changement de plugin de redirection, ou une modif de règles rewrite, c’est une source classique de 404. J’ajoute souvent wp rewrite flush --hard dans les scripts “migration/déploiement” pour éviter les surprises.
Comment exécuter WP‑CLI sur un hébergement mutualisé sans sudo ?
Vous l’installez dans votre $HOME/bin et vous exécutez WP‑CLI avec votre utilisateur. Le point critique devient la cohérence des permissions entre votre user et l’utilisateur PHP. Si PHP tourne avec votre user (souvent sur mutualisé), ça passe. Sinon, vous risquez des écritures impossibles.
WP‑CLI remplace-t-il un plugin de sauvegarde ?
Non. WP‑CLI est excellent pour automatiser des dumps DB et des opérations WordPress, mais une stratégie de sauvegarde sérieuse inclut stockage hors serveur, versioning, chiffrement, et tests de restauration. WP‑CLI est une brique, pas la stratégie entière.
Comment savoir si je cible la bonne installation WordPress ?
Je fais toujours un triplet de vérification avant toute commande destructive :
sudo -u www-data -H wp --path=/var/www/site core version
sudo -u www-data -H wp --path=/var/www/site option get home
sudo -u www-data -H wp --path=/var/www/site db size --all-tables
Que faire si une commande WP‑CLI “bloque” en cron mais pas en SSH ?
Écrivez les chemins absolus (/usr/local/bin/wp), fixez --path, redirigez stdout/stderr vers un log, et vérifiez l’environnement minimal du cron. Souvent, c’est juste un PATH différent ou un php non trouvé.
Est-ce que je dois activer WP_DEBUG pour WP‑CLI ?
Pas en continu. Activez-le temporairement si vous diagnostiquez une erreur, et logguez vers un fichier. Sur prod, gardez WP_DEBUG_DISPLAY à false et surveillez les logs.