Ce guide vous accompagne pour déplacer un site Joomla vers WordPress quand les deux vivent sur un compte d’hébergement cPanel/WHM. Bien des propriétaires arrivent ici avec un vieux site Joomla 3 qui ne reçoit plus de correctifs de sécurité, et ils craignent de devoir tout refaire à la main. Ce n’est pas le cas, parce que Joomla et WordPress gardent tous deux leur contenu dans une base de données MySQL que vous contrôlez entièrement. La réponse pratique est une conversion de données. Vous montez WordPress à côté du site Joomla en ligne, une extension d’importation lit la base Joomla et copie le contenu, puis une carte de redirections 301 garde les anciens liens fonctionnels. À la fin, WordPress sert le domaine, et les fichiers et la base de Joomla restent archivés en lieu sûr juste à côté.
Ce que vous allez accomplir
- Relever la version de Joomla, le préfixe des tables et les extensions installées, pour savoir ce qui se convertit tout seul et ce que vous refaites.
- Monter WordPress sur un sous-domaine du même compte cPanel pendant que Joomla continue de servir les visiteurs.
- Importer les articles, les catégories et les images depuis la base Joomla avec l’extension FG Joomla to WordPress.
- Écrire dans
.htaccessune carte de redirections 301 qui couvre les trois formes d’URL de Joomla, y compris les liens avec chaîne de requête. - Installer le nouveau site dans
public_htmlpendant une courte fenêtre de maintenance et confirmer que rien n’est brisé.
Prérequis
- Un compte cPanel qui héberge le site Joomla, avec accès aux écrans Bases de données MySQL et Sous-domaines de cPanel (ou un accès root à WHM).
- Un accès SSH avec WP-CLI, l’outil en ligne de commande de WordPress. Vérifiez avec
wp --info. Sans SSH, la plupart des étapes ont un équivalent plus lent dans le gestionnaire de fichiers de cPanel et dans phpMyAdmin. - Assez d’espace disque pour une deuxième copie de vos images. Mesurez le dossier
imagesavecdu -sh ~/public_html/images. - Un accès administrateur à l’arrière-boutique de Joomla, pour lire sa configuration globale et sa liste d’extensions.
- Une sauvegarde cPanel complète prise avant de commencer. Rien dans ce guide ne modifie la base Joomla, mais l’étape de bascule déplace des fichiers.
curlsur votre propre poste pour tester les redirections.
Les étapes supposent que Joomla se trouve dans public_html et que le nom d’utilisateur cPanel est exmpl. Remplacez ces valeurs par les vôtres partout où elles apparaissent.
Comment ça fonctionne
Joomla est une application PHP et MySQL autohébergée, la même base que WordPress. Les données sources dorment donc dans une base sur votre propre serveur, lisible par tout ce à quoi vous donnez accès. Vous n’avez pas besoin que Joomla fonctionne encore sur une version récente de PHP, ni de mettre Joomla à niveau d’abord. L’extension d’importation se contente de lire ses tables.
Les tables de Joomla partagent un préfixe, noté #__ dans la documentation de Joomla. Sur une vraie installation, c’est une courte chaîne aléatoire comme jx7k_. Les articles vivent donc dans jx7k_content, les catégories dans jx7k_categories et les utilisateurs dans jx7k_users. L’extension d’importation a besoin de ce préfixe, que vous trouvez dans configuration.php sous $dbprefix.
Pourquoi la version de Joomla compte encore. Joomla 3.x a atteint sa fin de vie (EOL, la date où les correctifs de sécurité s’arrêtent) en août 2023, et Joomla 4 a suivi en octobre 2025. Deux conséquences en découlent. D’abord, un site Joomla 3 non corrigé est peut-être déjà compromis, alors cherchez des fichiers implantés avant de copier quoi que ce soit. Ensuite, la structure des tables a changé d’une version à l’autre. Joomla 4, par exemple, ajoute un suffixe #joomlaImage:// aux valeurs des images d’introduction et d’article complet. Confirmez que la version de l’extension que vous installez prend en charge votre version de Joomla.
Ce qui se convertit, et ce qui ne se convertit pas. L’extension gratuite FG Joomla to WordPress, écrite par Frédéric Gilles, importe les catégories, les articles et les images qu’ils contiennent. L’édition Premium payante en ajoute davantage, dont les utilisateurs et les étiquettes, et des modules complémentaires existent pour certains composants tiers. Consultez la page de l’extension pour la liste à jour plutôt que de présumer. Certains éléments ne se convertissent jamais, peu importe la voie choisie :
- Les gabarits. Un gabarit Joomla est du PHP écrit pour Joomla. Vous choisissez ou construisez plutôt un thème WordPress.
- Les modules. Les blocs de barre latérale et de pied de page deviennent des widgets ou des blocs WordPress, placés à la main.
- Les extensions qui ont leurs propres données. Les articles K2, les boutiques VirtueMart et les profils Community Builder demandent un module complémentaire correspondant, ou une reconstruction sur un équivalent WordPress comme WooCommerce.
- Les menus. Premium gère certains éléments de menu ; les liens personnalisés vers des composants sont généralement à refaire.
L’autre voie consiste à écrire votre propre SQL pour copier les lignes de jx7k_content vers wp_posts. Ça fonctionne pour un tout petit site d’articles simples, sans images. Au-delà, vous réécririez ce que l’extension fait déjà : l’arbre des catégories, les chemins d’images, le texte d’introduction et les états de publication.
Étape par étape
-
Notez la version, le préfixe et les informations de base de données. Lisez-les directement dans les fichiers.
grep -m1 '<version>' ~/public_html/administrator/manifests/files/joomla.xml grep -E 'public \$(host|db|dbprefix|user) ' ~/public_html/configuration.phpRésultat attendu :
<version>3.10.12</version> public $host = 'localhost'; public $user = 'exmpl_jml'; public $db = 'exmpl_joomla'; public $dbprefix = 'jx7k_';Prenez-les en note. Si
$hostaffiche autre chose quelocalhost, la base vit sur un serveur distant, et c’est ce nom d’hôte que vous utiliserez dans l’extension. Si la première commande ne trouve aucun fichier, vous êtes sur Joomla 1.5, que le reste de ce guide ne couvre pas. -
Cherchez des fichiers PHP implantés. Le dossier
imagesde Joomla ne devrait contenir que des médias. Un fichier PHP à l’intérieur est un signe sérieux d’une intrusion passée.find ~/public_html/images -name '*.php'Résultat attendu : aucune sortie. Tout chemin affiché ici doit être examiné avant la migration, puisque l’extension copie les médias dans WordPress et que vous ne voulez pas y transporter une porte dérobée.
-
Mesurez le contenu et listez les extensions. Connectez-vous à MySQL avec l’utilisateur et le mot de passe Joomla de l’étape 1.
mysql -u exmpl_jml -p exmpl_joomla -e "SELECT state, COUNT(*) FROM jx7k_content GROUP BY state;"Résultat attendu :
+-------+----------+ | state | COUNT(*) | +-------+----------+ | -2 | 14 | | 0 | 9 | | 1 | 412 | | 2 | 37 | +-------+----------+L’état
1veut dire publié,0dépublié,2archivé et-2à la corbeille. Les nombres de publiés et d’archivés sont ceux que vous comparerez plus tard avec WordPress. Listez maintenant les composants actifs :mysql -u exmpl_jml -p exmpl_joomla -N -e "SELECT element FROM jx7k_extensions WHERE type='component' AND enabled=1 ORDER BY element;"La plupart des noms font partie du cœur de Joomla, comme
com_content,com_usersetcom_menus. Repérez les autres.com_k2,com_virtuemart,com_comprofiler(Community Builder) oucom_jeventssignalent chacun un bloc de contenu que l’extension gratuite n’importera pas. Notre inventaire avant migration couvre ce qu’il faut noter d’autre. -
Créez le WordPress en chantier sur un sous-domaine. Dans cPanel, ouvrez Domaines et créez
new.example.com. Sur les versions récentes de cPanel, sa racine de documents est~/new.example.compar défaut ; vérifiez le champ Racine de document et utilisez ce qu’il affiche. Créez ensuite une base vide et un utilisateur pour WordPress dans Bases de données MySQL, par exempleexmpl_wpetexmpl_wpuser. Installez WordPress à cet endroit :cd ~/new.example.com wp core download wp config create --dbname=exmpl_wp --dbuser=exmpl_wpuser --dbpass='REPLACE_ME' wp core install --url=https://new.example.com --title='Example Inc' \ --admin_user=admin --admin_email=you@example.com --skip-email wp rewrite structure '/%postname%/' --hardRésultat attendu :
Success: WordPress installed successfully.puisSuccess: Rewrite structure set.La structure/%postname%/donne à chaque article une URL tirée de son identifiant (le slug), et c’est elle que vise la carte de redirections. -
Donnez à l’utilisateur de la base WordPress un accès en lecture seule à Joomla. Les deux bases sont sur le même serveur MySQL, sous le même compte cPanel, alors une seule autorisation suffit. L’extension n’a besoin que de lire.
uapi Mysql set_privileges_on_database user=exmpl_wpuser database=exmpl_joomla privileges=SELECTLancez-la en SSH en tant qu’utilisateur cPanel ; si vous êtes connecté en root, ajoutez
--user=exmplaprèsuapi. Résultat attendu : un bloc YAML qui contientstatus: 1. Avec une autorisation limitée àSELECT, un mauvais réglage dans l’extension ne peut modifier aucune ligne de Joomla. -
Lancez l’importation. Installez et activez l’extension :
wp plugin install fg-joomla-to-wordpress --activateRésultat attendu :
Plugin 'fg-joomla-to-wordpress' activated.suivi deSuccess: Installed 1 of 1 plugins.Dans l’admin WordPress, ouvrez Outils > Importer et choisissez l’importateur Joomla (FG). Remplissez l’URL du site Joomla (https://example.com), l’hôte de base de donnéeslocalhost, la baseexmpl_joomla, l’utilisateurexmpl_wpuser, son mot de passe et le préfixe de tablesjx7k_. Testez la connexion, qui devrait réussir, puis lancez l’importation.L’extension télécharge les images depuis l’URL du site Joomla en ligne, et c’est pour ça que Joomla reste actif pendant cette étape. Quelques centaines d’articles prennent généralement plusieurs minutes. À la fin, comptez ce qui est arrivé :
wp post list --post_type=post --post_status=publish --format=count wp term list category --format=countRésultat attendu : un nombre d’articles proche de votre total de publiés de l’étape 3, et un nombre de catégories proche de celui de Joomla. Selon les réglages de l’extension, les articles archivés peuvent arriver publiés, alors comparez avec les deux nombres. Vérifiez quelques identifiants, puisque la carte de redirections en dépend :
wp post list --post_type=post --fields=ID,post_name --posts_per_page=3Résultat attendu : des identifiants comme
spring-maintenance-checklistqui correspondent à la colonnealiasdejx7k_content. Si votre base Joomla est enutf8et WordPress enutf8mb4, les caractères accentués peuvent arriver brouillés ; notre guide sur les jeux de caractères et les collations explique la correction. -
Cherchez les chemins d’images restés relatifs. Joomla enregistre souvent les images sous la forme
images/photo.jpg, sans nom d’hôte devant.wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_content LIKE '%src=\"images/%';" --skip-column-namesRésultat attendu :
0. Un nombre non nul veut dire que ces images n’ont pas été rapatriées. Relancez l’étape des médias de l’extension, ou copiez les fichiers danswp-content/uploadset corrigez les chemins avecwp search-replace --dry-rund’abord. -
Rassemblez les URL que les gens utilisent vraiment. Joomla sert le même article à jusqu’à trois adresses, selon ses réglages SEF (les URL optimisées pour les moteurs de recherche) :
/index.php?option=com_content&view=article&id=42&catid=8, quand SEF est désactivé./index.php/blog/42-spring-maintenance-checklist, quand SEF est activé mais sans réécriture d’URL./blog/42-spring-maintenance-checklistou/component/content/article/42-spring-maintenance-checklist, avec la réécriture activée. Certains sites ajoutent.html.
Voyez lesquelles votre trafic utilise, dans le journal d’accès de cPanel :
awk '$9==200 {print $7}' ~/access-logs/example.com-ssl_log \ | grep -vE '\.(css|js|png|jpe?g|gif|svg|woff2?|ico)' \ | sort | uniq -c | sort -rn | head -5Résultat attendu : une liste classée comme
318 /blog/42-spring-maintenance-checklist. Exportez aussi vos pages principales depuis Google Search Console, puisque le journal ne couvre que les derniers jours. -
Générez la carte de redirections. Exportez les identifiants et les alias des articles, puis transformez chacun en règles :
mysql -u exmpl_jml -p exmpl_joomla -N -e "SELECT id, alias FROM jx7k_content WHERE state IN (1,2) ORDER BY id;" > ~/joomla-articles.tsv awk -F'\t' '{ printf "RewriteRule ^(index\\.php/)?(.+/)?%s-%s(\\.html)?/?$ https://example.com/%s/ [R=301,L]\n", $1, $2, $2 printf "RewriteCond %%{QUERY_STRING} (^|&)id=%s(:|&|$)\n", $1 printf "RewriteCond %%{QUERY_STRING} (^|&)option=com_content(&|$)\n" printf "RewriteRule ^(index\\.php)?$ https://example.com/%s/? [R=301,L]\n", $2 }' ~/joomla-articles.tsv > ~/joomla-redirects.conf head -4 ~/joomla-redirects.confRésultat attendu :
RewriteRule ^(index\.php/)?(.+/)?42-spring-maintenance-checklist(\.html)?/?$ https://example.com/spring-maintenance-checklist/ [R=301,L] RewriteCond %{QUERY_STRING} (^|&)id=42(:|&|$) RewriteCond %{QUERY_STRING} (^|&)option=com_content(&|$) RewriteRule ^(index\.php)?$ https://example.com/spring-maintenance-checklist/? [R=301,L]La première règle capte toutes les formes de chemin, avec ou sans
index.php, un chemin de catégorie ou.html. Les trois suivantes gèrent les liens avec chaîne de requête. La directiveRedirectd’Apache ne compare que le chemin et ne voit jamais ce qui suit le?, alors ces liens exigentRewriteCond %{QUERY_STRING}. Le?à la fin de la cible retire l’ancienne chaîne de requête. La partie(:|&|$)compte, parce que Joomla écrit parfois l’identifiant sous la formeid=42:spring-maintenance-checklist.Si vos URL Joomla n’ont pas d’identifiant numérique, écrivez le chemin de catégorie en toutes lettres, comme
^(index\.php/)?blog/spring-maintenance-checklist/?$. Ne rendez jamais ce préfixe facultatif : la règle capterait la nouvelle adresse WordPress et tournerait en boucle.Collez le fichier dans
~/new.example.com/.htaccessau-dessus de# BEGIN WordPress, encadré comme ceci, avec des lignes écrites à la main pour les pages de catégorie :# --- Joomla legacy URLs: keep above the WordPress block --- RewriteEngine On RewriteRule ^(index\.php/)?blog/?$ https://example.com/category/blog/ [R=301,L] RewriteRule ^(index\.php/)?(.+/)?42-spring-maintenance-checklist(\.html)?/?$ https://example.com/spring-maintenance-checklist/ [R=301,L] # ...rest of joomla-redirects.conf... # --- end Joomla legacy URLs --- # BEGIN WordPressL’emplacement n’est pas négociable. Le bloc WordPress commence par
RewriteRule ^index\.php$ - [L], qui arrête le traitement pour toute requête versindex.php, alors les règles de chaîne de requête placées en dessous ne s’exécutent jamais. WordPress réécrit aussi tout ce qui se trouve entre ses marqueurs quand vous enregistrez les permaliens. -
Faites la bascule. Affichez un court avis sur le site Joomla s’il reçoit des commentaires ou des commandes, puis échangez les dossiers. Déplacer des fichiers dans le même compte est quasi instantané.
cd ~ mkdir joomla-archive mv public_html/* public_html/.htaccess joomla-archive/ mv new.example.com/* new.example.com/.htaccess public_html/ ls public_html | head -3Résultat attendu :
index.php,license.txt,readme.html. Retirez ensuite le nom d’hôte temporaire de la base WordPress :cd ~/public_html wp search-replace 'https://new.example.com' 'https://example.com' --all-tables wp option get siteurlRésultat attendu : un tableau des remplacements qui se termine par une ligne comme
Success: Made 1284 replacements., puishttps://example.com. Utilisez WP-CLI plutôt qu’unREPLACESQL, parce que WordPress stocke certains réglages en données PHP sérialisées. WP-CLI garde exacts les indicateurs de longueur de ces données. -
Gérez les comptes utilisateurs. Joomla 3.2 et les versions suivantes stockent les mots de passe sous forme de hachages bcrypt. WordPress 6.8 est aussi passé à bcrypt, mais il formate et vérifie les hachages à sa façon, alors ne tenez pas pour acquis qu’un hachage importé fonctionnera. Si vous avez importé des utilisateurs, testez un compte. Si la connexion échoue, envoyez un lien de réinitialisation à tout le monde :
wp user list --role=subscriber --field=ID | xargs wp user reset-passwordRésultat attendu : une ligne
Reset password for ...par utilisateur, puis une ligneSuccess:. Prévenez vos membres avant l’envoi des courriels, pour qu’ils ne les prennent pas pour de l’hameçonnage.
Valider que ça fonctionne
Testez chaque forme d’URL sur un article connu :
for u in '/blog/42-spring-maintenance-checklist' '/index.php/blog/42-spring-maintenance-checklist' '/index.php?option=com_content&view=article&id=42&catid=8'; do
printf '%s %s\n' "$(curl -s -o /dev/null -w '%{http_code} %{redirect_url}' "https://example.com$u")" "$u"
done
Résultat attendu : trois lignes, chacune commençant par 301 https://example.com/spring-maintenance-checklist/. Passez ensuite vos URL principales de l’étape 8 dans la même boucle et repérez tout ce qui n’est ni une 301 ni une 200. Confirmez qu’aucun contenu ne pointe encore vers les anciens chemins Joomla :
wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_status='publish' AND (post_content LIKE '%index.php?option=%' OR post_content LIKE '%src=\"images/%');" --skip-column-names
Résultat attendu : 0. Un nombre non nul correspond à des liens internes ou à des images qui visent encore Joomla. Nos tests de fumée après migration couvrent les formulaires, le courriel et la mise en cache le premier jour.
Dépannage
- L’extension n’arrive pas à se connecter à la base. L’utilisateur WordPress n’a pas l’autorisation de l’étape 5, ou le préfixe est erroné. Vérifiez avec
mysql -u exmpl_wpuser -p exmpl_joomla -e "SHOW TABLES LIKE 'jx7k_content';". La commande devrait afficher le nom de la table. Si elle n’affiche rien, le préfixe ne correspond pas à$dbprefix. - Les articles sont importés, mais les images manquent. L’extension récupère les médias par HTTP depuis l’URL Joomla que vous avez saisie. Une redirection de
httpvershttps, une vérification Cloudflare ou une protection contre le hotlinking dans.htaccesspeut bloquer ça. Saisissez l’adressehttps://exacte, désactivez temporairement la protection contre le hotlinking dans cPanel, puis relancez l’importation des médias. - Les anciennes URL renvoient une 404 après la bascule. Le bloc de redirections s’est retrouvé sous
# BEGIN WordPress, ou il est resté dans le.htaccessarchivé de Joomla. Comparez avecgrep -n 'BEGIN WordPress\|Joomla legacy' ~/public_html/.htaccess. Le numéro de ligne du bloc Joomla doit être le plus petit. - Une redirection tourne en boucle. Une règle sans identifiant ni préfixe de catégorie fixe capte sa propre cible. Lancez
curl -sILsur l’URL pour voir la boucle, puis rendez le préfixe de cette règle obligatoire. - Les caractères accentués sont brouillés. Les tables Joomla utilisent un autre jeu de caractères que WordPress. Corrigez le jeu de caractères à la source avant de réimporter, plutôt que de réparer les articles un par un.
Quand ce n’est pas la bonne approche
Si l’essentiel de la valeur du site vit dans une extension Joomla, l’importation des articles est la partie facile. Une boutique VirtueMart avec son historique de commandes ou une communauté Community Builder demande son propre plan de migration vers WooCommerce ou une extension de membres. Réglez ça en premier : un site dont les articles sont déplacés mais dont le paiement ne fonctionne pas n’est pas prêt à lancer.
Un petit site vitrine de dix pages, sans blogue, se refait souvent plus vite à la main dans WordPress. Gardez tout de même la carte de redirections de l’étape 9.
Et si le site fonctionne bien, n’a aucun problème de sécurité et que votre équipe est à l’aise dans Joomla, passer à une version prise en charge de Joomla 5 peut coûter moins cher qu’un changement de plateforme. Migrez parce que WordPress convient à vos prochaines années, pas seulement parce que Joomla 3 a vieilli. La page de l’extension FG Joomla to WordPress liste les versions prises en charge, et la référence mod_rewrite d’Apache documente RewriteCond et RewriteRule.
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.