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.yml et 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-replace avec --all-tables-with-prefix et 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 :

É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=json quand disponible, ou --porcelain quand WP‑CLI le propose, pour éviter de parser du texte humain.
  • Mode “non interactif” : ajoutez --yes aux 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 --path pointe ailleurs.
  • Permissions : des fichiers appartiennent à root, ou www-data n’a pas accès en écriture.
  • PHP CLI différent : WP‑CLI utilise /usr/bin/php (CLI), pas PHP-FPM.
  • DB host/socket : localhost peut utiliser un socket, 127.0.0.1 TCP. 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.log et /var/log/wp-deploy-site.log avec rotation logrotate.
  • Secrets : évitez d’écrire des mots de passe DB en clair dans vos scripts. Préférez lire depuis wp-config.php via 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

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.