
🔄 WordPress dans un sous-dossier : migrer l'installation depuis la racine et inversement
Situation connue: vous avez monté un site de développement, intégré le thème, transféré le contenu via WP Migrate DB Pro, et après le push vous découvrez des styles cassés, des images perdues et un wp-admin qui ne fonctionne pas. La cause est presque toujours la même: la production est à la racine, et le dev, dans un sous-dossier (ou l'inverse), et un simple search-replace d'URL dans la base ne corrige pas cette différence.
Le problème est plus profond qu'il n'y paraît: dans une installation à la racine, tous les liens WordPress (vers les pages comme vers les fichiers médias) utilisent le même domaine. Dans une installation en sous-dossier, les liens vers le contenu partent de l'adresse du site, et les liens vers les ressources (css, js, images), de l'adresse de WordPress. Un search-replace ordinaire sur la base remplace tout de la même façon et casse la moitié des chemins.
Dans ce guide, deux itinéraires de migration éprouvés (aller et retour) avec des réglages de search-replace concrets, la préparation de wp-config.php et le bon ordre de transfert des fichiers. Après lecture, vous amènerez soit le dev et la production à un schéma unique, soit vous mènerez sciemment une migration entre installations de types différents sans frontend cassé.
💡 Aperçu rapide:
- Déterminez le type d'installation: «Adresse de WordPress» et «Adresse du site» coïncident-elles dans Réglages → Général
- Pour migrer d'un sous-dossier vers la racine: codez en dur WP_SITEURL dans wp-config, effectuez le remplacement
/subdir→/dans la base de données, transférez les fichiers d'un niveau au-dessus, mettez à jour l'index.php racine - Pour migrer de la racine vers un sous-dossier: remplacez dans la base seulement les chemins vers
/wp-content, mettez à jour l'adresse de WordPress dans les réglages, créez le sous-dossier, copiez index.php et .htaccess de retour dans la racine - Après toute migration, allez dans Réglages → Permaliens et cliquez sur «Enregistrer»: cela reconstruira la structure des URL et videra le cache
Comment déterminer où WordPress est installé
Si vous avez installé WordPress manuellement, vous vous souvenez sûrement si c'était la racine du domaine ou un sous-dossier du type /wp ou /blog. Mais si le site est hérité d'un développeur précédent, déployé par l'hébergement en un clic, ou que plusieurs années ont passé, les détails s'effacent.
Le moyen le plus rapide: allez dans l'admin WordPress, ouvrez Réglages → Général et regardez les champs «Adresse web de WordPress (URL)» et «Adresse web du site (URL)». Si les valeurs coïncident, vous avez devant vous une installation à la racine:

Si les champs diffèrent, WordPress est installé dans un sous-dossier (dans l'exemple ci-dessous, c'est /subdir):

Signe supplémentaire d'une installation en sous-dossier: à la connexion à l'admin, l'URL contient le sous-dossier, par exemple, example.com/wp/wp-admin/ au lieu de example.com/wp-admin/.
Pourquoi on ne peut pas simplement transférer
La racine du problème, c'est le double système d'URL que WordPress utilise lors d'une installation en sous-dossier. Décomposons sur des exemples concrets.
Supposons que vous ayez une installation à la racine sur example.com. Absolument tous les liens dans la base de données, vers l'article /2025/about-page comme vers l'image /wp-content/uploads/photo.jpg, commencent par //example.com. Un search-replace ordinaire //example.local → //example.com fonctionne parfaitement.
Prenons maintenant une installation dans le sous-dossier /wp. Le lien vers le même article ressemble à //example.com/about-page (via l'adresse du site), et le lien vers la même image, à //example.com/wp/wp-content/uploads/photo.jpg (via l'adresse de WordPress avec le sous-dossier). Un simple remplacement //example.local → //example.com cassera les fichiers médias: le système les cherchera sans /wp dans le chemin et recevra un 404.
Le tableau ci-dessous montre quels groupes d'URL il faut mettre à jour dans chaque direction de migration:
Direction | URL des pages et articles | URL des médias et ressources | Chemins vers les fichiers dans la BD |
|---|---|---|---|
Sous-dossier → racine | Remplacer | Remplacer | Remplacer |
Racine → sous-dossier | Laisser tel quel | Remplacer | Remplacer |
Outre la base de données, il faut physiquement déplacer les fichiers et mettre à jour index.php dans la racine, sinon WordPress ne trouvera pas wp-blog-header.php. Voyons ensuite les deux itinéraires étape par étape.
Méthode 1: transfert de WordPress d'un sous-dossier vers la racine
C'est la direction la plus simple: vous retirez le sous-dossier des chemins, et toutes les URL deviennent «plates», comme dans une installation standard.
Étape 0: diagnostic de ce qui va mal tourner
Avant d'intervenir, il est utile de voir l'ampleur du problème de ses propres yeux. Sur la capture ci-dessous, les réglages de migration de wp-in-a-subdirectory.local (WordPress dans /subdir) vers wp-standard-install.local (installation à la racine). Les réglages de WP Migrate DB Pro, standard, plus le remplacement du titre du site pour la démonstration:

Le résultat est comme prévu déplorable: les pages s'ouvrent, mais sans styles et avec des images cassées:

Dans le HTML, on voit des liens vers les ressources avec le chemin mort /subdir, qui n'existe déjà plus sur le serveur cible. La tentative d'accéder à wp-admin provoque une redirection vers wp-standard-install.local/subdir/wp-login.php, or un tel fichier n'existe pas. Réparons maintenant.
Étape 1: préparation
Tout d'abord, protégeons l'accès à l'admin pendant la migration. Ajoutez à wp-config.php des constantes qui redéfiniront les réglages de la base de données, ainsi WordPress continuera à vous laisser entrer dans l'admin par l'ancien chemin avec le sous-dossier, même après le nettoyage de la base:
1 define( 'WP_SITEURL', 'http://wp-in-a-subdirectory.local/subdir' ); 2 define( 'WP_HOME', 'http://wp-in-a-subdirectory.local' );
Ensuite, mettez le site en maintenance: modifiez index.php dans la racine publique, commentez la ligne require( dirname( __FILE__ )... et, après la balise fermante ?>, insérez un bouchon html avec un message d'indisponibilité brève. Les visiteurs verront ceci:

Vous continuez pendant ce temps à entrer dans l'admin à l'adresse http://wp-in-a-subdirectory.local/subdir/wp-admin/, la constante WP_SITEURL fonctionne.
Étape 2: recherche et remplacement dans la base de données
Nettoyons maintenant la base. Lancez un search-replace avec ces paires (montré dans l'interface de WP Migrate DB Pro, mais le même principe fonctionne avec WP-CLI search-replace ou des requêtes SQL via phpMyAdmin):
//wp-in-a-subdirectory.local/subdir→//wp-in-a-subdirectory.local/app/public/subdir→/app/public(chemin vers les fichiers sur le serveur)

Juste après la migration, l'apparence ne changera pas, la page de maintenance reste affichée, l'admin fonctionne via la constante codée en dur. Mais si l'on regarde le contenu des articles, les images ne se chargent pas encore, et les liens internes ont «perdu» le sous-dossier, c'est précisément ce que nous visions à cette étape:

Étape 3: transfert physique des fichiers
Supprimez (ou commentez) les lignes avec WP_SITEURL et WP_HOME de wp-config.php. Maintenant l'admin va tomber, et transférez aussitôt les fichiers du sous-dossier d'un niveau au-dessus.
Via SSH ou la ligne de commande sur le serveur, cela se fait en trois commandes:
1 rm index.php && mv subdir/* . && rm -rf subdir
Via FTP ou le gestionnaire de fichiers de l'hébergement, glissez tout le contenu du sous-dossier dans la racine publique en remplaçant index.php:

Voilà. Le site s'ouvre par l'URL racine, images et styles sont en place:

Dernière touche: allez dans l'admin (c'est maintenant http://wp-in-a-subdirectory.local/wp-admin sans sous-dossier), ouvrez Réglages → Permaliens et cliquez sur «Enregistrer les modifications», même si vous n'avez rien changé. WordPress reconstruira la structure des URL et videra le cache.
Méthode 2: transfert de WordPress de la racine vers un sous-dossier
Beaucoup de développeurs considèrent l'installation de WordPress dans un sous-dossier comme une bonne pratique: les fichiers du cœur n'encombrent pas la racine, la gestion via Git/Composer est simplifiée, et le domaine lui-même peut servir à d'autres applications. Mais la migration d'un site existant vers un sous-dossier est objectivement plus complexe que l'inverse, car désormais une partie des liens DOIT conserver le sous-dossier, et une autre, non.
Étape 0: diagnostic
Le même point de départ: on tente une migration standard de l'installation racine wp-standard-install.local vers celle en sous-dossier wp-in-a-subdirectory.local (WordPress dans /subdir):

Le résultat est tout à fait attendu: les pages s'ouvrent, mais les styles et les images sont cassés, /subdir n'a pas été ajouté à leur chemin:

Contrairement au premier scénario, les liens vers les articles et les pages fonctionnent correctement, ils ne doivent justement pas contenir le sous-dossier. Ce sont précisément les ressources (css, js, médias) qui cassent, dont les chemins doivent désormais inclure /subdir.
Étape 1: préparation
Les constantes WP_SITEURL et WP_HOME à cette étape, on ne les inscrit PAS, notre search-replace ne touchera pas à ces valeurs, et nous mettrons à jour l'adresse de WordPress manuellement un peu plus tard.
La page de maintenance, on la met de la même façon: on commente require(...) dans index.php et on ajoute un bouchon html. Les visiteurs voient un message de maintenance, et vous continuez à entrer dans l'admin par http://wp-standard-install.local/wp-admin/.
Étape 2: search-replace sélectif dans la base
La différence clé avec la première méthode: nous remplaçons SEULEMENT les chemins vers les fichiers et les ressources, sans TOUCHER aux URL des pages. Pour cela, on cible le remplacement par le masque /wp-content:
//wp-standard-install.local/wp-content→//wp-standard-install.local/subdir/wp-content/app/public→/app/public/subdir(chemin sur le serveur)

Après la migration, on vérifie le contenu des articles: les liens vers d'autres pages du site ne contiennent PAS le sous-dossier (correct), tandis que les images intégrées, si (correct aussi):

Étape 3: mise à jour de l'adresse de WordPress et transfert des fichiers
Allez maintenant dans Réglages → Général et ajoutez le sous-dossier à la fin de «Adresse web de WordPress (URL)», par exemple, http://wp-standard-install.local/subdir. Juste après l'enregistrement, l'admin va tomber, car WordPress tentera de trouver les fichiers par le nouveau chemin, or ils n'y sont pas encore:

Créez le sous-dossier subdir dans la racine publique et déplacez-y TOUS les fichiers de WordPress. Ensuite, copiez index.php et .htaccess de RETOUR dans la racine, pour que la page de maintenance reste affichée pendant que nous terminons:

Restaurez index.php À L'INTÉRIEUR du sous-dossier à son état d'origine, retirez le bouchon html et décommentez la ligne avec require:
1 <?php 2 define( 'WP_USE_THEMES', true ); 3 require( dirname( __FILE__ ) . '/wp-blog-header.php' );
Maintenant l'admin est de nouveau accessible à l'adresse http://wp-standard-install.local/subdir/wp-admin/:

Vérifiez le contenu, les images sont en place, les styles se chargent:

Touche finale: l'index.php racine
Il reste à mettre à jour index.php dans la racine publique. Retirez la page de maintenance et inscrivez le chemin actuel vers wp-blog-header.php en tenant compte du sous-dossier:
1 <?php 2 define( 'WP_USE_THEMES', true ); 3 require( dirname( __FILE__ ) . '/subdir/wp-blog-header.php' );
Le site s'ouvre par l'URL racine, toutes les ressources sont récupérées depuis le sous-dossier:

Allez de nouveau dans Réglages → Permaliens et enregistrez sans modification, pour que WordPress rafraîchisse la structure des URL.
Outils alternatifs et méthode officielle
L'approche décrite ci-dessus avec WP Migrate DB Pro est pratique, mais pas la seule. Voici avec quoi vous pouvez travailler aussi:
WP-CLI
search-replace. La commandewp search-replace '//oldsite.local/subdir' '//newsite.com'avec le drapeau--dry-runmontrera d'abord combien d'occurrences seront remplacées. Pour un remplacement sélectif (racine → sous-dossier), affinez le masque:wp search-replace '//newsite.com/wp-content' '//newsite.com/subdir/wp-content'.Méthode officielle de WordPress. La documentation developer.wordpress.org décrit la procédure «Giving WordPress Its Own Directory», avec des configurations détaillées pour Apache (.htaccess), nginx (server block) et IIS (web.config). La méthode ne requiert pas d'extensions et fonctionne sur n'importe quel hébergement.
SQL manuel. Si le volume de modifications est faible, on peut exécuter
UPDATE wp_posts SET post_content = REPLACE(post_content, '/subdir/', '/')directement dans phpMyAdmin, mais impérativement avec une sauvegarde préalable, une telle substitution détruira les données sérialisées dans wp_options et wp_postmeta.
Quel que soit l'outil que vous choisissez, la règle est une: lors d'une migration RACINE → SOUS-DOSSIER, remplacez seulement /wp-content et les chemins de fichiers; lors d'une migration SOUS-DOSSIER → RACINE, remplacez tout ce qui renvoie au sous-dossier.
⁉️🤔 Questions fréquentes
Est-il obligatoire d'utiliser WP Migrate DB Pro pour une telle migration?
Non. WP Migrate DB Pro donne simplement une interface pratique pour le search-replace avec la compréhension des données sérialisées de PHP. Techniquement, vous pouvez effectuer les mêmes remplacements via WP-CLI (la commande
wp search-replacetraite aussi correctement les serialized strings) ou utiliser la méthode officielle de WordPress avec transfert manuel des fichiers et modification d'index.php. L'extension économise du temps sur les projets grands et moyens, où il y a beaucoup d'occurrences.
Que faire si, après la migration, une partie des images ne se charge toujours pas?
La cause la plus fréquente: il reste dans la base des URL codées en dur avec les chemins absolus de l'ancien serveur, qui ne sont pas tombés sous le masque de remplacement. Vérifiez le contenu des articles via phpMyAdmin:
SELECT ID, post_content FROM wp_posts WHERE post_content LIKE '%/subdir/%'(ou l'ancien domaine). Deuxième candidat, le cache du navigateur et le CDN: videz-les et vérifiez en navigation privée.
Faut-il mettre à jour le.htaccess après le transfert?
Si vous utilisez des permaliens jolis, oui, mais WordPress le fait lui-même quand vous cliquez sur «Enregistrer» sur la page Réglages → Permaliens. Si le serveur n'a pas les droits d'écriture, WordPress affichera le contenu du.htaccess prêt à l'emploi, copiez-le manuellement. Lors d'une migration vers un sous-dossier, assurez-vous que le.htaccess racine (et non celui à l'intérieur du sous-dossier) ne contient pas de règles en conflit avec la nouvelle structure.
Peut-on effectuer la migration sans aucun temps d'arrêt?
Techniquement oui, en utilisant la méthode avec redirections.htaccess (Method I de la documentation officielle de WordPress, «Sans changer les URL»). Dans cette approche, les fichiers sont transférés dans le sous-dossier, et le.htaccess racine dirige de façon transparente toutes les requêtes vers le nouvel emplacement. Les visiteurs ne remarquent pas le déménagement. Inconvénient: vous restez sur le même domaine, et l'URL du site ne change pas formellement (le sous-dossier n'est pas visible dans la barre d'adresse).
Pourquoi WP Migrate DB Pro ne prend-il pas en charge la migration entre installations de types différents d'origine?
Les développeurs de Delicious Brains en ont discuté sur GitHub pendant presque trois ans. La racine du problème: l'extension applique UNE paire search-replace à TOUTE la base, alors que pour une migration entre root et subdirectory il faut des remplacements DIFFÉRENTS pour différents groupes d'URL (pages vs ressources). La détermination automatique de quel URL appartient à quel groupe exigerait de parser la structure du contenu, ce qui dépasse le cadre d'un simple search-replace. C'est pourquoi la recommandation actuelle: amenez les sites à un schéma d'installation unique AVANT la migration.

Au final: racine ou sous-dossier?
Le choix entre une installation de WordPress à la racine et en sous-dossier se ramène, au fond, à un seul compromis. L'installation à la racine est plus simple: moins de pièces mobiles, compatibilité directe entre dev et production, aucune surprise avec les doubles URL. Celle en sous-dossier, plus propre sur le plan architectural: les fichiers du cœur sont isolés, dans la racine ne se trouve que index.php, il est plus facile de mettre à jour WordPress via Git/Composer et plus sûr d'héberger plusieurs applications sur un même domaine.
Si vous avez un site de production et un site de dev, amenez les deux à un schéma unique (n'importe lequel) et oubliez le problème. Si vous travaillez en équipe, où une partie des projets est historiquement à la racine et une autre, en sous-dossier, vous connaissez maintenant les masques exacts de search-replace pour chaque direction.
La règle d'or à mettre en favori: lors d'une migration RACINE → SOUS-DOSSIER, ne touchez qu'à /wp-content et aux chemins de fichiers; lors d'une migration SOUS-DOSSIER → RACINE, remplacez tout ce qui contient le sous-dossier. Et toujours, toujours, cliquez sur «Enregistrer» dans les Permaliens après le déménagement.



