OLYMPUS

Formations

Mini-formation · tiré de ~25 conversations

WordPress en série : WP-CLI, cPanel et n8n

Comment tu pilotes des sites WordPress par script : un fichier PHP qui lance WP-CLI, appelé par n8n, pour installer, mettre à jour et nettoyer chaque site, plus les erreurs de chemin et de fichiers que tu as croisées.

~25 conversations6 modules16 mois~20 min de lecture
n8nPOST ?ma_fonction_wpcli=…auto.php (clé + liste blanche)shell_exec · wp …Site A · wp_pathSite B · wp_pathSite C · wp_pathun wp_path par site, chemins absolus
00

Le principe

ObjectifComprendre la chaîne n8n → PHP → WP-CLI et pourquoi elle est construite ainsi.

WP-CLI exécute des commandes WordPress en ligne de commande. Sur ton hébergement mutualisé, tu n'as pas toujours de SSH pratique : tu as donc déposé un fichier `auto.php` qui reçoit une requête, vérifie une clé, choisit la commande dans une liste blanche et lance wp ... avec shell_exec() dans le bon dossier. n8n appelle ce fichier avec ma_fonction_wpcli=<action> et un wp_path par site.

n8nPOST ?ma_fonction_wpcli=…auto.php (clé + liste blanche)shell_exec · wp …Site A · wp_pathSite B · wp_pathSite C · wp_pathun wp_path par site, chemins absolus
Un script central, N sites : seul le wp_path change d'un appel à l'autre.
Squelette du script (idée retenue)
<?php
if (!hash_equals($cle_attendue, $_REQUEST['key'] ?? '')) { http_response_code(403); exit; }

$commandes = [
  'update_plugins' => 'plugin update --all',
  'update_core'    => 'core update',
  'status'         => 'core check-update',
];
$cmd = $_REQUEST['ma_fonction_wpcli'] ?? '';
if (!isset($commandes[$cmd])) { http_response_code(400); exit('Commande non autorisée'); }

$sortie = shell_exec(sprintf('cd %s && /usr/local/bin/wp %s 2>&1',
  escapeshellarg($wp_path), $commandes[$cmd]));
  • Liste blanche obligatoire : jamais une commande libre passée dans l'URL.
  • escapeshellarg() sur chaque valeur venant de la requête.
  • Réponse JSON propre : ob_clean(), header('Content-Type: application/json'), display_errors à 0, sinon n8n reçoit du HTML et casse.

Pour vérifier

Pourquoi passer par une liste blanche de commandes ?

01

Installer un site de bout en bout

ObjectifEnchaîner les étapes d'une création de site sans toucher à l'interface.
  1. Créer base, utilisateur et domaine via l'API cPanel (o2switch).

  2. Téléverser et extraire WordPress (FTP ou API Fileman).

  3. wp core download --locale=fr_FR si les fichiers ne sont pas déjà posés.

  4. wp config create avec nom de base, utilisateur, mot de passe, hôte.

  5. wp core install avec l'URL, le titre et le compte administrateur.

  6. Thème et plugins depuis tes ZIP locaux, puis réglages (permaliens…).

Commandes déjà prévues dans ton fichier

Action n8nCommande WP-CLI
wp_get_versioncore version
wp_get_statutcore check-update
wp_updatecore update
wp_get_permalien / wp_update_permalienoption get|update permalink_structure
wp_restoredb import %s
wp_core_download, wp_config_create, wp_core_installcore download, config create, core install
« Le dossier du site n'existe pas : /pix-flow.com/public_html »
CauseLe chemin FTP est relatif à la racine FTP, pas un chemin serveur.
SolutionPréfixe avec le home : /home/<utilisateur>/pix-flow.com/public_html. Rends le PHP tolérant : teste aussi home + chemin.
« Fichier wp-config.php manquant » alors que le fichier existe
CauseLe script cherche dans un autre dossier que celui du site (même problème de wp_path).
SolutionJournalise le chemin testé, vérifie ls <wp_path>/wp-config.php côté serveur.
ftp_mkdir(): Can't create directory
CauseCréation d'un dossier dont le parent n'existe pas (pas de mkdir récursif en FTP).
SolutionCrée les dossiers un niveau à la fois, ou corrige le site_path (/public_html selon la racine FTP).

Pour vérifier

Le script dit « dossier du site inexistant » avec un chemin qui commence par `/pix-flow.com/…`. Que faire ?

02

Extraire et placer les fichiers (API cPanel)

ObjectifÉviter le double dossier public_html/exemple.fr/public_html/exemple.fr/wordpress.

L'archive WordPress contient un dossier wordpress/. Si tu extrais dans un chemin déjà complet et que destfiles répète ce chemin, tu obtiens des dossiers imbriqués. Extrais dans le dossier du site avec `destfiles: "."`, puis déplace le contenu de wordpress/.

Déplacer le contenu (Fileman::fileop, API v2)
{ "cpanel_jsonapi_module": "Fileman", "cpanel_jsonapi_func": "fileop",
  "dir": "/public_html/exemple.fr/wordpress",
  "op": "move", "sourcefiles": "*", "destfiles": "/public_html/exemple.fr" }

// puis les fichiers cachés (.htaccess…) avec "sourcefiles": ".*"
// puis "op": "trash" sur le dossier wordpress vide
En-tête « authorization: » invalide (ERR_INVALID_HTTP_TOKEN)
CauseDeux-points dans le nom de l'en-tête n8n.
SolutionNom : Authorization ; valeur : cpanel UTILISATEUR:JETON.
403 « Access denied » sur l'API cPanel
CauseJeton sans droit sur le module ou IP non autorisée.
SolutionVérifie les privilèges du jeton et le module appelé.
03

Mettre à jour thèmes et plugins depuis tes ZIP

ObjectifDéployer ton thème (ex. Pegasus) et tes plugins maison sur chaque site.

wp theme install refuse un thème déjà présent (« le dossier existe déjà »). Ton besoin est une mise à jour depuis un ZIP local : tu as donc écrit theme_update et plugins_update sur le même modèle.

  • Le ZIP porte le nom du thème/plugin (/themes/Pegasus-1.6.zip, /plugins/pix-up.zip) et contient un dossier au vrai slug.
  • Supprimer l'ancien dossier, puis wp theme install <zip> --force --activate.
  • Pour un plugin : wp plugin install <zip> --force --activate --skip-themes.
  • Résoudre le ZIP avec realpath(__DIR__ . "/../plugins/{$name}.zip") depuis includes/plugins.php ; sinon « ZIP introuvable ».
`plugins_install` installe un plugin du dépôt WordPress.org
CauseLa commande reçoit le nom pix-up et non le chemin du ZIP.
SolutionRemplacer par l'installation depuis le ZIP local.
« Thème non trouvé » après la mise à jour
CauseLe dossier interne du ZIP n'a pas le même nom que celui attendu par theme activate.
SolutionAligner le nom du ZIP, du dossier interne et du paramètre name.

Pour vérifier

Pourquoi `wp theme install` seul ne suffit pas pour mettre à jour ton thème ?

04

Alléger l'administration

ObjectifRetirer ce que tu n'utilises pas, par un petit plugin maison.
  • Masquer le type de contenu Projets du thème (menu admin) : léger gain, surtout de la clarté.
  • Désactiver les éditeurs de fichiers thème/extensions : define('DISALLOW_FILE_EDIT', true); dans wp-config.php est plus fiable que de masquer des liens.
  • Retirer le menu Commentaires si le site n'en a pas.
  • Supprimer le panneau de bienvenue (welcome-panel) via le hook dédié.

Piège constaté : masquer un lien de menu en CSS ou avec remove_submenu_page laisse l'URL accessible. Seule la constante bloque réellement l'accès.

05

Gérer un parc de sites

ObjectifRendre la maintenance de plusieurs milliers de sites supportable.

Constat de départ : un parc d'environ 4 000 sites au même ensemble de plugins, dont le vrai problème est le design (pages modifiées, CSS custom) et non les mises à jour techniques.

PrincipeMise en œuvre
Un thème parent communLes écarts de design passent par des tokens CSS et des presets, pas par du CSS écrit site par site
Plugin « Master » légerIl applique les tokens, charge les presets autorisés, vérifie la licence et parle à ton API
Intelligence au centreMoteur de génération CSS, presets premium, mapping site → design et versions restent sur ton serveur
Protection du codeImpossible à 100 % si le code tourne chez le client : licence par domaine, réponses signées, mise à jour depuis un dépôt privé, pas de secret en dur

Le plugin devient un exécuteur local ; la valeur (presets, règles, tableau de bord) reste côté API. C'est la direction du registre « Plugins & thèmes » de l'application WP.

Pour vérifier

Où placer ce qui fait la valeur de ton plugin Master ?

Source : conversations WP-CLI, installation WordPress via cPanel/FTP, mises à jour de thèmes et plugins, nettoyage de l'admin et maintenance d'un parc (juin 2025 à octobre 2026). Les extraits sont des modèles à adapter ; ne mets jamais de clé réelle dans l'URL d'un exemple.
Neo