# Synchroniser deux bases WordPress avec search-replace sans tout casser

> Un remplacement d'URL mal fait peut casser des menus, des champs ACF et des blocs entiers. Voici la méthode qu'on suit pour synchroniser une base sans mauvaise surprise.

- Auteur : Clément Hadrot
- Publié le : 2020-12-09
- Mis à jour le : 2020-12-09
- Catégorie : Outils &amp; workflow
- URL : https://wpmoderne.dev.wordpress-developpement.fr/outils/search-replace-synchroniser-bases-wordpress/

## L’essentiel

- Toujours exporter avant de toucher à la base
- --dry-run pour vérifier avant d'exécuter
- Attention aux données sérialisées dans les remplacements bruts

Synchroniser une base de production vers un environnement local, ou inversement pousser un environnement de staging vers la production, passe presque toujours par une étape de remplacement d'URL. C'est une opération qui paraît anodine et qui, mal exécutée, peut corrompre discrètement des dizaines de champs sans qu'on s'en rende compte avant plusieurs jours.

Le problème vient de la sérialisation PHP. WordPress stocke énormément de données sous forme de chaînes sérialisées — les widgets, certains réglages de thème, beaucoup de champs de plugins comme Advanced Custom Fields. Une chaîne sérialisée contient la longueur exacte de chaque valeur. Si on remplace le texte sans recalculer cette longueur, la chaîne devient invalide et WordPress ne sait plus la relire.

## Pourquoi un remplacement SQL classique est dangereux

La méthode intuitive consiste à faire un `UPDATE` SQL direct avec `REPLACE()` :

```
UPDATE wp_options SET option_value = REPLACE(option_value, 'http://site.local', 'https://site.fr');
```

Sur une colonne texte simple, ça fonctionne. Sur une valeur sérialisée comme `a:2:{s:3:"url";s:19:"http://site.local";...}`, remplacer la chaîne de caractères change sa longueur sans mettre à jour le `s:19` qui la précède. Résultat : PHP échoue à désérialiser la valeur, et le réglage concerné disparaît silencieusement, parfois seulement visible plusieurs semaines après.

## La bonne méthode avec WP-CLI

`wp search-replace` résout exactement ce problème : il comprend la sérialisation PHP et recalcule les longueurs correctement. C'est la méthode qu'on utilise systématiquement, sans exception, même pour un remplacement qui paraît trivial.

```
# Étape 1 : toujours un export de sauvegarde avant de toucher à quoi que ce soit
wp db export backup-avant-remplacement.sql

# Étape 2 : simulation, sans rien modifier
wp search-replace 'http://site.local' 'https://site.fr' --dry-run --report-changed-only

# Étape 3 : exécution réelle
wp search-replace 'http://site.local' 'https://site.fr' \
  --skip-columns=guid --all-tables --precise
```

> L'essentiel à retenir : Toujours exporter avant de toucher à la base ; --dry-run pour vérifier avant d'exécuter ; Attention aux données sérialisées dans les remplacements bruts

L'option `--skip-columns=guid` évite de modifier la colonne `guid` des articles, qui doit rester stable dans le temps une fois publiée — c'est une recommandation officielle de WordPress, indépendante de l'environnement. `--all-tables` étend la recherche aux tables créées par des plugins qui ne suivent pas le préfixe standard, utile sur des installations avec plusieurs plugins d'e-commerce ou de formulaires.

## Le cas particulier du multisite

Sur une installation multisite, chaque site a ses propres tables avec un identifiant numérique (`wp_2_options`, `wp_3_posts`, etc.). Un remplacement mal ciblé peut affecter tous les sites du réseau au lieu d'un seul :

```
wp search-replace 'http://site1.local' 'https://site1.fr' --url=site1.local --network
```

L'option `--url` restreint l'opération au site concerné ; `--network` indique qu'on travaille dans un contexte multisite. Sans ces précisions, WP-CLI peut se tromper de contexte et traiter la mauvaise base de tables.

## Vérifier après coup

- `wp option get siteurl` et `wp option get home` pour confirmer que les URL de base sont correctes
- Un tour rapide sur les widgets et les menus, souvent les premiers touchés en cas de remplacement raté
- Les champs ACF de type image ou relation, qui stockent parfois des identifiants sérialisés sensibles à la casse

Sur un projet avec beaucoup de contenu généré par des blocs Gutenberg, on vérifie aussi que le contenu des articles ne contient pas d'URL en dur dans les attributs de bloc — `search-replace` les traite correctement, mais un export/import mal fait via un autre outil peut les laisser de côté.

> Notre règle non négociable : jamais de remplacement SQL brut sur une base WordPress. Si `wp search-replace` n'est pas disponible pour une raison quelconque, on préfère écrire un script PHP qui utilise les fonctions de sérialisation natives plutôt que de risquer une base corrompue.

## Automatiser la synchronisation complète

Sur nos projets suivis dans la durée, on combine cette étape avec un export/import scripté pour synchroniser rapidement la production vers le local :

```
#!/bin/bash
ssh utilisateur@serveur "wp db export - " > dump-prod.sql
wp db import dump-prod.sql
wp search-replace 'https://site.fr' 'http://site.local' --skip-columns=guid
wp cache flush
```

Ce petit script fait gagner un temps considérable sur les projets où on a besoin de tester régulièrement avec des données réelles en local, tout en gardant la base de production totalement intacte — l'opération ne fait que lire, jamais écrire sur le serveur distant.

## En résumé

Synchroniser des bases WordPress entre environnements n'a rien de compliqué à condition de passer systématiquement par `wp search-replace`, de sauvegarder avant chaque opération, et de toujours tester en `--dry-run` d'abord. C'est une discipline simple à mettre en place, et elle évite l'un des incidents les plus frustrants du développement WordPress : une base qui semble fonctionner, mais dont des réglages entiers ont disparu sans message d'erreur.
