Aller au contenu

WP Engine : pousser la préproduction vers la production sans rien perdre

Sur WP Engine, une copie écrase la destination dans un seul sens, elle ne fusionne rien. Ce que transporte vraiment une poussée de la préproduction vers la production, pourquoi inclure la base sur un site vivant détruit commandes et comptes, et le déroulé WP-CLI pour promouvoir sans casse.

Sur WP Engine, pousser la préproduction vers la production tient en un bouton — Copy environment dans le portail client — et presque tout le risque tient à une seule option de cet écran. Une copie écrase la destination dans un seul sens, elle ne fusionne jamais : les fichiers envoyés remplacent ceux qui étaient là, et si vous incluez la base, chaque ligne écrite en production depuis votre instantané part avec eux — commandes, comptes clients, commentaires, formulaires, sans restauration partielle ensuite. Pour un site qui rapporte, la réponse est donc presque toujours une copie de fichiers seulement, plus une migration ciblée des seules tables et options modifiées par la livraison, la copie complète restant réservée aux sites où vous seul écrivez des lignes. Ce guide est le déroulé propre à WP Engine ; pour la forme générale du problème, indépendante de l’hébergeur, voyez ce qui circule vraiment entre préproduction et production.

Ce que vous allez accomplir

  • Une promotion exécutable sur un site WP Engine vivant sans perdre ce que la production a écrit pendant votre travail en préproduction.
  • Une règle claire : quand une copie complète avec la base est acceptable, et quand elle ne l’est pas.
  • Une promotion de fichiers seulement, plus une migration ciblée des tables et options touchées par la livraison.
  • Une liste de réconciliation pour tout ce que la copie ne transporte pas : redirections, caches, cron, clés d’API, webhooks, robots.txt, SSL et mappage de domaine.
  • Une vérification menée par la passerelle SSH de WP Engine, et un retour arrière qui reste peu coûteux en cas d’échec.

Prérequis

  • L’accès aux deux environnements dans le portail client WP Engine, avec le droit de lancer une copie — les droits sont par utilisateur, le bouton peut donc être absent chez vous.
  • Votre clé publique SSH ajoutée à votre profil WP Engine. La cible de la passerelle est <nom-environnement>@<nom-environnement>.ssh.wpengine.net.
  • Sachez quels noms d’hôte par défaut portent vos environnements avant de commencer. Les comptes actuels reçoivent <nom-environnement>.wpenginepowered.com, les plus anciens <nom-environnement>.wpengine.com. Les exemples ci-dessous utilisent l’ancienne forme — remplacez-la par celle que votre compte émet.
  • La passerelle SSH est restreinte, ce n’est pas un shell général. Elle exécute WP-CLI, rsync, git et quelques commandes liées à l’intérieur de votre installation — pas de sudo, pas de gestionnaire de paquets, aucun accès hors du site. Chaque commande ci-dessous est donc lancée en une fois avec ssh <cible> "wp ...".
  • WP-CLI est fourni par la plateforme ; vous ne l’installez pas. Confirmez avec ssh <cible> "wp --info".
  • Le nombre d’environnements et certains contrôles du portail varient selon le forfait. La plupart donnent la production plus la préproduction et le développement, mais vérifiez votre compte plutôt que d’en supposer trois.

Comment ça fonctionne

WP Engine modélise un site comme trois installations distinctes au plus — développement, préproduction et production — chacune avec son arborescence, sa base, son domaine et son cache. Ce ne sont pas des branches d’une même chose : rien n’est partagé, rien ne se synchronise tout seul. Le seul déplacement prévu est une copie, et une copie remplace la destination.

Schéma des environnements de développement, de préproduction et de production WP Engine avec les flèches de copie annotées par ce que chacune transporte, et un marqueur d'avertissement sur le chemin de base préproduction vers production

Deux conséquences en découlent, et elles font tout l’article. D’abord, une copie ne fusionne jamais : il n’y a pas de comparaison à trois voies entre les deux bases, seulement un remplacement, table par table. Ensuite, une copie réécrit le domaine du site dans la base qu’elle vient de déplacer, parce qu’une base de préproduction est remplie du nom d’hôte de préproduction.

Les options de copie, en clair

Ce que vous choisissezCe que ça fait à la destinationSûr sur une production vivante ?
Tous les fichiersL’arborescence de la destination est remplacée par celle de la source, wp-content compris. Ne supposez pas que les fichiers présents seulement à la destination — téléversements ajoutés depuis l’instantané — y survivent.Seulement si les téléversements sont inchangés ou sauvegardés
Thèmes et extensions seulementLe code sous wp-content/themes et wp-content/plugins est remplacé ; les téléversements ne sont pas touchés.Habituellement oui
Base incluseLa base de la destination est remplacée par celle de la source, puis le domaine est réécrit.Presque jamais
Base exclueLes fichiers bougent, les données de la destination restent intactes.Oui — c’est le choix par défaut

Les libellés de cet écran ont changé au fil des versions du portail et certains choix dépendent du forfait : rapprochez ce qui est affiché chez vous de la colonne de comportement, pas de la formulation ici. S’il demande de taper le nom de l’environnement de destination, c’est la plateforme qui vous dit que l’étape est irréversible.

Ce qu’une copie de base détruit sur la cible

Une poussée complète remplace les lignes de la production par un instantané pris quand vous avez copié la production vers la préproduction, peut-être il y a des jours. Tout ce qui a été enregistré entre les deux est supprimé :

  • Les commandes. Avec le stockage haute performance de WooCommerce, elles vivent dans wp_wc_orders et ses tables compagnes ; sur les boutiques plus anciennes, ce sont des lignes shop_order dans wp_posts et wp_postmeta. Dans les deux cas elles partent, avec leurs métadonnées de paiement.
  • Comptes, commentaires et soumissions de formulaire. Tout compte et tout envoi depuis l’instantané.
  • Les identifiants séquentiels. Les compteurs auto-incrémentés reculent avec les tables : les nouvelles commandes réutilisent des numéros déjà envoyés par courriel à de vrais clients — factures en double et problèmes de rapprochement qui survivent longtemps à la panne.
  • Le travail planifié. Les lignes d’Action Scheduler, les renouvellements, tout ce qui est en file dans la base.
  • Les modifications éditoriales. Les articles modifiés dans l’admin pendant votre fenêtre de préproduction n’existent qu’en production et n’ont aucune fusion automatique.

Une boutique active ne devrait pour ainsi dire jamais recevoir une poussée de base complète. Si vous en avez besoin, c’est la livraison qui pose problème : quelque chose se configure dans un écran d’admin au lieu d’être du code ou un script de migration.

La réécriture du domaine et le piège des données sérialisées

WordPress stocke beaucoup d’options et de métadonnées en tableaux sérialisés PHP, où chaque chaîne porte sa longueur en octets : s:23:"https://old.example.com". Un simple UPDATE ... REPLACE() en SQL change la chaîne mais laisse le 23 derrière. PHP refuse alors de désérialiser la valeur et ne renvoie rien en silence — les widgets disparaissent, les réglages de thème se réinitialisent, les mises en page des constructeurs reviennent vides.

wp search-replace existe exactement pour ça : il parcourt chaque colonne, désérialise, remplace à l’intérieur de la structure et resérialise avec les longueurs corrigées. Lancez-le d’abord avec --dry-run, toujours. La référence WP-CLI search-replace documente les options.

Ce que la copie ne transporte pas

Ces éléments appartiennent à l’environnement et se réconcilient à la main après chaque promotion :

  • Les règles de redirection configurées dans le portail client appartiennent à l’environnement, pas au site copié.
  • Le cache de pages et le CDN. La destination sert l’ancienne réponse tant que vous ne purgez pas.
  • La configuration propre à l’environnement. WP Engine gère une partie de la configuration plateforme par environnement ; vérifiez les constantes après coup au lieu de supposer que wp-config.php a voyagé intact.
  • L’état du cron. Les événements copiés dans une base peuvent porter des arguments du mauvais environnement, et un verrou doing_cron périmé bloque la file.
  • Les transients et le cache objet. Ils contiennent des valeurs calculées pour un autre site.
  • Les clés d’API et les webhooks tiers. Une copie de base amène les clés de test de la préproduction en production et laisse votre fournisseur de paiement ou de CRM poster ses webhooks sur le mauvais nom d’hôte.
  • robots.txt et l’indicateur noindex. WP Engine sert les environnements hors production avec un en-tête de réponse noindex, mais blog_public vit dans la base — une base de préproduction qui atterrit en production sort le site vivant de l’index.
  • Le SSL et le mappage de domaine. Domaines personnalisés, certificats, utilisateurs SFTP et clés SSH restent attachés à l’environnement.

Étape par étape

Lisez ceci avant de lancer quoi que ce soit. Une copie qui inclut la base remplace purement et simplement les tables de la production. Pas de fusion, pas d’annulation partielle, pas de retour arrière ligne par ligne : les commandes, les comptes, les commentaires et les envois de formulaire écrits depuis votre instantané de préproduction sont perdus, et les compteurs auto-incrémentés reculent avec eux. Restaurer un point de sauvegarde WP Engine ne rattrape pas ça non plus — c’est un autre écrasement complet de l’environnement, qui jettera tout ce qui est arrivé après. L’étape 2 n’est donc pas facultative, et l’export qu’elle produit doit vivre ailleurs que sur la plateforme.

Les étapes 1 à 4 sont réversibles. À partir de l’étape 5, non.

Schéma du déroulé de la liste de contrôle de promotion : sauvegarde préalable, gel, copie, réconciliation de ce qui ne voyage pas, vérification, dégel, avec l'étape irréversible marquée

  1. Confirmez sur quel environnement vous êtes pointé. Chaque commande destructrice ci-dessous n’est qu’à une faute de frappe de la mauvaise installation.

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp option get siteurl; wp option get home"
    https://example.com
    https://example.com

    Si l’une des lignes affiche un nom d’hôte par défaut de WP Engine — .wpenginepowered.com sur les comptes actuels, .wpengine.com sur les plus anciens — vous êtes en préproduction ou en développement : corrigez la cible avant de continuer. Sur les forfaits supérieurs, la préproduction peut porter un domaine personnalisé, et le nom d’hôte seul ne vous dira alors rien : recoupez le nom d’environnement de la cible SSH avec le portail.

  2. Prenez votre propre sauvegarde, hors de la plateforme. Créez aussi un point de sauvegarde dans le portail, mais n’en faites pas votre seule copie.

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp db export - --add-drop-table" > prod-preflight-2026-09-09.sql
    ls -lh prod-preflight-2026-09-09.sql
    tail -1 prod-preflight-2026-09-09.sql
    -rw-r--r-- 1 deploy deploy 412M Sep  9 09:14 prod-preflight-2026-09-09.sql
    -- Dump completed on 2026-09-09  9:14:07

    wp db export - écrit le SQL sur la sortie standard et n’affiche volontairement aucune ligne de succès : une première commande silencieuse est donc le bon résultat. Le vrai signal, ce sont les deux vérifications suivantes : une taille plausible et une ligne finale -- Dump completed on .... Un export qui se termine autrement a été coupé — n’allez pas plus loin avec celui-là, et gardez le fichier ailleurs que sur la plateforme.

  3. Enregistrez une référence à laquelle comparer ensuite.

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp plugin list --status=active --field=name" | sort > prod-plugins-before.txt
    ssh exampleprod@exampleprod.ssh.wpengine.net "wp user list --format=count; wp comment count"
    1284
    approved:        3312
    spam:            41
    trash:           2
    post-trashed:    0
    all:             3355
    moderated:       0
    total_comments:  3355

    user list --format=count affiche un entier ; comment count affiche une ligne clé : valeur par catégorie, et se termine par total_comments. Pour une boutique, ajoutez un décompte des commandes — wp db query "SELECT COUNT(*) FROM wp_wc_orders;" avec le stockage haute performance, wp post list --post_type=shop_order --post_status=any --format=count avec l’ancienne disposition. Confirmez d’abord le préfixe des tables.

  4. Gelez la destination. Un instantané qui continue de bouger ne peut pas être réconcilié.

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp maintenance-mode activate; wp maintenance-mode status"
    Enabling Maintenance mode...
    Success: Activated Maintenance mode.
    Maintenance mode is active.

    Purgez immédiatement le cache de pages depuis le portail, sinon les pages en cache restent servies et les acheteurs continuent de commander. Le mode maintenance n’interrompt pas un paiement en cours : choisissez une heure vraiment calme.

  5. Lancez la copie — fichiers seulement, chaque fois que possible. Dans le portail client, démarrez la copie depuis l’environnement source, choisissez la production comme destination, retenez l’option de fichiers décidée plus haut et laissez la base de côté. Confirmez en tapant le nom de l’environnement de destination. Comparez ensuite la liste des extensions :

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp plugin list --status=active --field=name" | sort > prod-plugins-after.txt
    diff prod-plugins-before.txt prod-plugins-after.txt

    N’attendez aucune sortie. C’est correct, et c’est le piège : l’état d’activation des extensions vit dans la base, donc une nouvelle extension arrive sous forme de fichiers mais reste inactive. Activez-la explicitement :

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp plugin activate example-release-plugin"
    Plugin 'example-release-plugin' activated.
    Success: Activated 1 of 1 plugins.
  6. Ne migrez que les données modifiées par la livraison. Exportez les tables que votre livraison a créées ou altérées, et n’importez que celles-là :

    ssh examplestg@examplestg.ssh.wpengine.net "wp db export - --tables=wp_examplerelease_rules,wp_examplerelease_map" > release-tables.sql
    ssh exampleprod@exampleprod.ssh.wpengine.net "wp db import -" < release-tables.sql
    Success: Imported from '-'.

    Déplacez les réglages isolés une option à la fois plutôt que de toucher wp_options en entier — cette table contient aussi siteurl, home, blog_public et tous les identifiants :

    ssh examplestg@examplestg.ssh.wpengine.net "wp option get example_theme_settings --format=json" > settings.json
    ssh exampleprod@exampleprod.ssh.wpengine.net "wp option update example_theme_settings --format=json" < settings.json
    Success: Updated 'example_theme_settings' option.
  7. Seulement si vous avez inclus la base : vérifiez d’abord la réécriture. Ce chemin s’adresse aux sites à faible trafic où personne d’autre que vous n’écrit de lignes.

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp option get siteurl; wp option get home; wp option get blog_public"
    https://example.com
    https://example.com
    1

    Un 0 sur la dernière ligne, c’est l’indicateur noindex de la préproduction posé sur votre site vivant. Si un nom d’hôte de préproduction subsiste ailleurs, testez d’abord la correction à blanc :

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp search-replace 'https://examplestg.wpengine.com' 'https://example.com' --all-tables --precise --dry-run --report-changed-only"
    +-------------+--------------+--------------+------+
    | Table       | Column       | Replacements | Type |
    +-------------+--------------+--------------+------+
    | wp_options  | option_value | 27           | PHP  |
    | wp_postmeta | meta_value   | 214          | PHP  |
    +-------------+--------------+--------------+------+
    Success: 241 replacements to be made.

    Lisez la liste avant de retirer --dry-run et de la lancer pour de vrai. Ne faites jamais ça avec un REPLACE() SQL.

  8. Réconciliez ce que la copie n’a pas transporté.

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp transient delete --all"
    ssh exampleprod@exampleprod.ssh.wpengine.net "wp cache flush"
    ssh exampleprod@exampleprod.ssh.wpengine.net "wp cron event list --fields=hook,next_run_relative --format=table"

    transient delete --all indique combien il en a supprimé. cache flush affiche Success: The cache was flushed. quand un cache objet est présent, et un avis sinon ; les deux conviennent. Le tableau du cron doit montrer les tâches planifiées du cœur de WordPress avec des délais plausibles, et des entrées loin dans le passé signalent une file bloquée. Ensuite, dans le portail : purgez tous les caches, revérifiez les redirections, remettez les clés d’API de production, repointez les webhooks des fournisseurs sur le nom d’hôte de production, et confirmez que le mappage de domaine, le certificat et la version de PHP correspondent à ce qu’avait la production.

  9. Dégelez, puis surveillez.

    ssh exampleprod@exampleprod.ssh.wpengine.net "wp maintenance-mode deactivate"
    Disabling Maintenance mode...
    Success: Deactivated Maintenance mode.

    Purgez le cache une fois de plus pour que les visiteurs reçoivent les nouvelles pages, puis surveillez le journal d’erreurs et le flux de commandes une heure avant de déclarer que c’est fini.

Valider que ça fonctionne

La production ne doit pas porter d’en-tête noindex, et la préproduction doit en porter un :

curl -sI https://example.com/ | grep -i -E '^HTTP|x-robots-tag'
HTTP/2 200
curl -sI https://examplestg.wpengine.com/ | grep -i x-robots-tag
x-robots-tag: noindex, nofollow

Un x-robots-tag sur la réponse de production, c’est précisément l’échec que cet article cherche à éviter. La valeur exacte hors production peut différer : retenez le signal « présent en préproduction, absent en production » plutôt que la formulation. Confirmez ensuite que la base est d’accord avec l’en-tête :

ssh exampleprod@exampleprod.ssh.wpengine.net "wp option get blog_public"
ssh exampleprod@exampleprod.ssh.wpengine.net "wp plugin list --status=active --field=name" | sort | diff prod-plugins-before.txt -
curl -s https://example.com/robots.txt | head -5

blog_public doit afficher 1, le diff ne doit montrer que les extensions activées volontairement à l’étape 5, et robots.txt ne doit pas tout interdire. Enfin, faites ce pour quoi le site existe : passez une vraie commande ou envoyez le formulaire, puis relancez les décomptes de l’étape 3 et confirmez qu’ils ont monté, pas reculé.

Dépannage

L’admin redirige vers le domaine de préproduction, ou la connexion boucle. siteurl et home contiennent encore le nom d’hôte de préproduction, ou des constantes les écrasent. Vérifiez les deux couches : wp option get siteurl et wp config get WP_HOME --type=constant. Les constantes l’emportent : corriger l’option seule ne suffira pas.

Les widgets, réglages de thème ou mises en page reviennent vides. Des valeurs sérialisées ont été cassées par un remplacement qui ignore les longueurs en octets. Il n’existe pas de passe de réparation : restaurez l’export de l’étape 2 et refaites la réécriture avec wp search-replace --precise.

Le site vivant disparaît des résultats de recherche quelques jours plus tard. Une base de préproduction a atterri en production en apportant blog_public = 0. Remettez-le avec wp option update blog_public 1, purgez les caches et demandez une réindexation ; la reprise n’est pas immédiate.

search-replace annonce zéro remplacement. La chaîne cherchée ne correspond pas exactement. Récupérez sa forme réelle avec wp option get home sur la source — le protocole, le www et la barre oblique finale comptent tous.

wp db export est refusé ou expire sur un gros site. La passerelle est restreinte et les commandes longues peuvent être coupées. Repliez-vous sur un point de sauvegarde du portail et découpez l’export avec --tables=.

Quand cette approche n’est pas la bonne

Une boutique très active ne devrait pas utiliser de copie d’environnement complète. Si des commandes arrivent pendant la fenêtre, vous ne pouvez pas en faire une, et la réponse est une chaîne de déploiement : le code dans Git, les changements de schéma en scripts de migration idempotents, les réglages modifiés par le code plutôt que par des écrans d’admin. Une copie est un outil de répétition et de secours, pas un processus de livraison.

C’est également la mauvaise approche quand les rédacteurs traitent la préproduction comme une seconde production. S’ils y rédigent du contenu en s’attendant à le voir en ligne, une promotion perdra un côté du travail quel que soit le sens de la poussée.

Enfin, si ce que vous voulez est un contrôle que WP Engine n’offre pas — écrans de cron, boîtes de courriel, extensions PHP arbitraires, un vrai shell — c’est une décision d’hébergement, et déplacer le site vers un hébergeur cPanel est le déroulé qu’il vous faut. La documentation de soutien de WP Engine fait autorité sur le comportement actuel du portail, qui change.

Un WordPress qui rapporte vraiment mérite un opérateur — Stack Harbor opère cette pile sous WordPress Entreprise ; réservez une consultation d’infrastructure.