Convertir le charset MySQL en utf8mb4 pour WordPress

Credit : Logo officiel

Convertir le charset MySQL en utf8mb4 pour WordPress

Dylan D. — Agent Support Technique Serveur Web 2238 mots 12 min de lecture

Convertir le charset MySQL en utf8mb4 pour WordPress

Un ticket récurrent au support, et toujours pour la même raison : un client tape un emoji dans un titre d'article WordPress, sauvegarde, et l'emoji disparaît, remplacé par un ? ou par une chaîne vide qui tronque le titre. Ou alors le visiteur voit s'afficher des ?? à la place des caractères chinois dans une fiche produit. Dans 100% des cas le coupable est le même : la base MySQL est encore en charset utf8 (le faux UTF-8 de MySQL), au lieu de utf8mb4 (le vrai UTF-8 complet).

Dans cet article je te montre comment diagnostiquer le problème, comment convertir proprement tes tables sans rien casser, les pièges à connaître, et comment vérifier que tout est OK après. Spoiler : ça prend 10 minutes pour une base WordPress moyenne, et ça règle définitivement la question.

L'arnaque historique du charset utf8 dans MySQL

C'est l'un des plus gros pièges historiques de MySQL, qui a coûté des heures à des milliers de devs. Voici la vérité.

Lorsque MySQL a introduit le support UTF-8 en 2003 (version 4.1), l'équipe a fait un choix qui paraissait raisonnable à l'époque : limiter les caractères à 3 octets maximum par caractère, parce que ça suffisait pour le BMP (Basic Multilingual Plane) qui couvrait toutes les langues vivantes.

Problème : le vrai standard UTF-8 définit jusqu'à 4 octets par caractère pour couvrir les plans supplémentaires d'Unicode. Et c'est précisément dans ces plans qu'on trouve :

Quand MySQL a finalement ajouté le support des 4 octets en 2010 (version 5.5.3), ils ont créé un nouveau charset appelé utf8mb4 (mb4 pour "multibyte 4") au lieu de corriger utf8. Pour des raisons de rétrocompatibilité, utf8 est resté l'alias historique de utf8mb3 (3 octets max).

Résultat : toutes les bases créées avant 2015 sont probablement en utf8 (donc utf8mb3), et toutes les bases ne supportant pas les emojis dans WordPress souffrent de ce problème.

Note importante : à partir de MySQL 8.0, l'équipe a finalement décidé que utf8 deviendrait à terme un alias pour utf8mb4. Mais pour l'instant, dans la majorité des installations en production en 2026, utf8 reste équivalent à utf8mb3. Mieux vaut être explicite.

Pourquoi WordPress est concerné

WordPress utilise utf8mb4 par défaut depuis la version 4.2 (avril 2015). Toutes les nouvelles installations WordPress depuis cette date créent leurs tables en utf8mb4_unicode_ci.

Mais ! Si ton site a été :

Alors tes tables peuvent encore être en utf8. Et tu peux même avoir un cas hybride où certaines tables sont en utf8mb4 et d'autres en utf8, ce qui crée des bugs subtils sur les jointures.

Diagnostiquer le charset actuel

Première chose à faire : voir où tu en es exactement.

Charset au niveau du serveur

Dans phpMyAdmin onglet SQL, ou via une connexion MySQL en ligne de commande :

SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';

Tu verras des variables comme character_set_server, character_set_database, character_set_client, collation_database. Sur un serveur moderne MariaDB 10.6+ ou MySQL 8.0+, tu devrais voir utf8mb4 partout.

Charset des tables

C'est là que ça devient intéressant. Pour ta base WordPress :

SELECT
  TABLE_NAME,
  TABLE_COLLATION,
  ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'dbs1234567'
ORDER BY TABLE_NAME;

Remplace dbs1234567 par le nom de ta base. Tu obtiens un tableau avec chaque table et sa collation actuelle.

Les collations qui posent problème :

Les collations correctes :

Charset des colonnes

Même au sein d'une table en utf8mb4, certaines colonnes peuvent être en utf8 si elles ont été ajoutées avec un CHARACTER SET explicite :

SELECT
  TABLE_NAME,
  COLUMN_NAME,
  CHARACTER_SET_NAME,
  COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'dbs1234567'
  AND CHARACTER_SET_NAME IS NOT NULL
  AND CHARACTER_SET_NAME != 'utf8mb4'
ORDER BY TABLE_NAME, COLUMN_NAME;

Si cette requête renvoie des lignes, ce sont les colonnes à corriger individuellement après la conversion des tables.

Backup obligatoire avant conversion

Je ne le répéterai jamais assez : avant toute opération ALTER TABLE sur une base de production, fais un dump complet. Toujours.

Via mysqldump en SSH :

mysqldump -u dbu1234567 -p -h db5004789543.hosting-data.io \
  --default-character-set=utf8mb4 \
  --single-transaction \
  --routines \
  --triggers \
  dbs1234567 > backup_avant_conversion_$(date +%Y%m%d_%H%M%S).sql

Les options expliquées :

Via phpMyAdmin si pas de SSH : sélectionne la base, Exporter, méthode Personnalisée, format SQL, charset utf8mb4, télécharge le fichier.

Vérifie ensuite que le dump est utilisable :

ls -lh backup_avant_conversion_*.sql
head -50 backup_avant_conversion_*.sql | grep -i charset

Tu dois voir des lignes du type /*!40101 SET NAMES utf8mb4 */; au début. Si c'est le cas, ton backup est bon.

Convertir une table à la fois

La syntaxe de base :

ALTER TABLE wp_posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Ce qui se passe sous le capot :

  1. MySQL crée une copie temporaire de la table
  2. Il lit chaque ligne, convertit les colonnes texte vers le nouveau charset
  3. Il remplace l'ancienne table par la nouvelle
  4. Le verrou est détenu pendant l'opération (lecture + écriture bloquées sur cette table)

Pour une table de 100 000 lignes, compte 30 secondes à 2 minutes selon le serveur. Pour wp_postmeta qui peut faire des millions de lignes, ça peut prendre 10-15 minutes.

Différence subtile mais cruciale

Il y a deux syntaxes qui se ressemblent mais font des choses différentes :

-- Change uniquement le CHARSET PAR DEFAUT pour les futures colonnes
-- Les colonnes existantes gardent leur ancien charset
ALTER TABLE wp_posts CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Change le charset par défaut ET convertit toutes les colonnes existantes
ALTER TABLE wp_posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Toujours utiliser CONVERT TO sauf si tu sais exactement ce que tu fais. La première syntaxe peut laisser des colonnes individuelles en utf8 même après la commande.

Convertir toutes les tables en une fois

Écrire une commande ALTER pour chaque table c'est fastidieux. Génère-les automatiquement :

SELECT CONCAT(
  'ALTER TABLE `', TABLE_NAME,
  '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
) AS statement
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'dbs1234567'
  AND TABLE_COLLATION NOT LIKE 'utf8mb4%'
  AND TABLE_TYPE = 'BASE TABLE';

Cette requête te génère la liste des ALTER TABLE à exécuter, uniquement pour les tables qui ne sont pas déjà en utf8mb4. Copie le résultat (toute la colonne statement), colle dans une nouvelle requête SQL, et exécute.

Sur une base WordPress standard avec 12 tables, l'ensemble prend généralement moins de 5 minutes.

Adapter wp-config.php

Une fois la base convertie, vérifie que WordPress utilise bien le bon charset à la connexion. Dans wp-config.php :

define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', 'utf8mb4_unicode_ci');

Si tu vois define('DB_CHARSET', 'utf8'); ou pire define('DB_CHARSET', 'latin1');, change-le immédiatement. Le DB_COLLATE peut rester vide ('') si tu veux laisser MySQL utiliser la collation par défaut de la base, mais l'expliciter c'est plus propre.

Précautions et limitations à connaître

Limite de longueur des index

C'est le piège le plus fréquent lors d'une conversion. Avec utf8mb3, un caractère prend max 3 octets. Avec utf8mb4, un caractère prend max 4 octets. Donc une colonne VARCHAR(255) qui faisait 255 * 3 = 765 octets passe à 255 * 4 = 1020 octets.

Problème : la limite d'index InnoDB par défaut est de 767 octets en MySQL 5.6 et antérieur, et de 3072 octets en MySQL 5.7+ avec innodb_large_prefix=ON.

Sur les versions modernes (MySQL 5.7+, MariaDB 10.2+) c'est résolu par défaut, tu n'as rien à faire. Sur les versions anciennes ou avec config bizarre, tu peux voir une erreur du type :

ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes

Solution : activer le format de fichier Barracuda et le large prefix :

SET GLOBAL innodb_file_format = Barracuda;
SET GLOBAL innodb_large_prefix = ON;
SET GLOBAL innodb_default_row_format = DYNAMIC;

Puis convertir les tables avec ROW_FORMAT=DYNAMIC :

ALTER TABLE wp_posts ROW_FORMAT=DYNAMIC;
ALTER TABLE wp_posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Augmentation de la taille de la base

Les données existantes ne grossissent pas (les caractères ASCII font toujours 1 octet en utf8mb4), mais les index peuvent prendre 33% de plus en théorie. En pratique sur une base WordPress moyenne, l'augmentation totale est de 5 à 15%.

Performance

Utf8mb4 est légèrement plus lent que utf8mb3 sur les comparaisons et les tris (parce que les caractères font potentiellement 4 octets au lieu de 3). En pratique sur WordPress c'est totalement imperceptible, sauf sur des bases avec des centaines de millions de lignes.

Vérifier que la conversion a marché

Après conversion, relance la requête de diagnostic :

SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'dbs1234567'
  AND TABLE_COLLATION NOT LIKE 'utf8mb4%';

Si cette requête retourne zéro ligne, toutes tes tables sont en utf8mb4. Vérifie aussi les colonnes :

SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'dbs1234567'
  AND CHARACTER_SET_NAME IS NOT NULL
  AND CHARACTER_SET_NAME != 'utf8mb4';

Zéro ligne aussi = tout est bon.

Test fonctionnel

Le vrai test : crée un nouvel article WordPress dont le titre contient un emoji (par exemple Test emoji rocket), sauvegarde, regarde le rendu sur le front. Si l'emoji s'affiche, c'est gagné. Si tu vois ?? ou un titre tronqué, il reste un souci à creuser.

Erreurs courantes et leur fix

Erreur 1 : ERROR 1071 specified key was too long

Limite d'index dépassée sur MySQL 5.6 ou MariaDB 10.1. Active innodb_large_prefix et innodb_file_format=Barracuda, et convertis avec ROW_FORMAT=DYNAMIC. Si tu peux, mets à jour MySQL ou MariaDB vers une version plus récente où ces réglages sont par défaut.

Erreur 2 : caractères chinois affichés en ??? malgré utf8mb4

La table est bien en utf8mb4 mais les données stockées sont déjà corrompues. La conversion ALTER ne peut pas réparer des données déjà mal encodées (par exemple stockées en latin1 dans une colonne utf8). Il faut soit restaurer un dump antérieur à la corruption, soit ré-encoder les données via un script.

Erreur 3 : la conversion plante sur wp_options

La table wp_options peut contenir des valeurs sérialisées (dans les colonnes option_value). En théorie l'ALTER ne casse rien parce qu'il convertit juste l'encodage. En pratique sur des bases anciennes mal entretenues, certaines valeurs sérialisées contenaient déjà des octets binaires invalides en UTF-8. Diagnostic : exporte cette table à part avant conversion, regarde si tu as des valeurs anormales.

Erreur 4 : DB_CHARSET oublié dans wp-config.php

Tu as converti la base, mais WordPress se connecte toujours en utf8 parce que DB_CHARSET est resté à utf8 ou n'est pas défini. Du coup les nouveaux emojis sont quand même tronqués à l'écriture. Vérifie et corrige.

Erreur 5 : MariaDB 10.6 default utf8mb4_general_ci

Sur MariaDB 10.6+, la collation par défaut est utf8mb4_general_ci, alors que WordPress préfère historiquement utf8mb4_unicode_ci. Les deux sont valides utf8mb4. La différence : unicode_ci gère mieux le tri des caractères accentués (l'ordre des é, è, ê dans un ORDER BY est plus correct). Pour un site WordPress en français, je recommande utf8mb4_unicode_ci. Pour un site international, utf8mb4_unicode_520_ci est encore mieux.

Pour aller plus loin

La gestion des bases MySQL c'est un sujet vaste. Voici les articles connexes utiles :

Le check-up MySQL que je fais à chaque audit

À chaque fois que je reprends en main un site WordPress dont la maintenance n'est pas claire, je fais systématiquement le diagnostic charset des tables. Tu serais étonné du nombre de bases qui sont encore en utf8 voire en latin1 en 2026 chez des clients dont le site fonctionne très bien en apparence. Pas de bug visible parce qu'ils n'ont jamais utilisé d'emoji ou de caractère exotique.

Mon conseil : convertis dès maintenant, même si tu n'as pas de problème apparent. Le jour où ton client veut publier un titre avec un emoji ou un caractère cyrillique, le bug arrivera, et le client te tiendra responsable de ne pas l'avoir vu venir. Cinq minutes de prévention valent mieux que deux heures de correction sous pression. Et avec MariaDB 10.6+ et MySQL 8.0+ qui font tout en utf8mb4 par défaut, il n'y a aucune raison de garder l'ancien charset défaillant.

# Articles similaires

Sur les memes sujets et plus loin