2026年5月26日

2026年5月26日

WordPressのデータベース文字化けを直す方法|原因の見分け方とutf8mb4への修復

WordPress の日本語が文字化けしたときは、まずデータベースに「どんなバイト列が保存されているか」を確かめます。同じ文字化けに見えても、原因によって正しい直し方が逆になるためです。たとえば、よく紹介される ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 は、データの状態によっては文字化けを固定してしまいます。

この記事の SQL は MySQL 8.4 で動作を確かめています(2026年11月時点)。MariaDB でも同じ構文で使えます。

作業の前にバックアップを取る

文字コードの修復はデータそのものを書き換えます。失敗したときに戻せるよう、先にデータベース全体をエクスポートしておきます。

# WP-CLI が使える場合
wp db export before-charset-fix.sql

# mysqldump を使う場合
mysqldump -u ユーザー名 -p データベース名 > before-charset-fix.sql

可能であれば、本番ではなく複製した環境で手順を試してから本番に適用してください。

文字化けの見た目で原因の見当をつける

見た目 主な原因 戻せるか
ã‚¢ 日本 のような欧文記号の並び UTF-8 の文字を latin1 として扱った(二重エンコード) 戻せる
??? や ? 表せない文字コードに変換され、文字が ? に置き換わった 戻せない(バックアップから復元)
� や読めない漢字の並び Shift_JIS と UTF-8 の取り違え(テーマのファイルやダンプファイルの保存形式) ファイル側を直す

? に置き換わったデータは、元の文字の情報が残っていないため、SQL では戻せません。文字化けする前のバックアップから復元します。

テーマやプラグインのファイルが Shift_JIS で保存されている場合は、データベースではなくファイルの問題です。WordPressのShift_JISファイルをUTF-8に変換する方法を参照してください。

保存されているバイト列を確かめる

phpMyAdmin の SQL タブ、または WP-CLI の wp db query で、文字化けしている投稿の中身を16進数で表示します。テーブル接頭辞が wp_ 以外なら読み替えてください。

SELECT ID, post_title, HEX(post_title)
FROM wp_posts
WHERE ID = 123;

ひらがなの「あ」は UTF-8 では E38182 の3バイトです。結果を次のように読みます。

  • E38182… のように UTF-8 のバイト列がそのまま入っている → データは正しい。表示側(接続の文字コード、列の文字コードの宣言)の問題
  • C3A3C281… のように、1文字が5〜6バイトに膨らんでいる → UTF-8 がさらにもう一度 UTF-8 に変換されて保存されている(二重エンコード済み)
  • 3F(?)が並んでいる → 文字が失われている

あわせて、テーブルと列の文字コードを確認します。

-- テーブルの照合順序
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE();

-- 列ごとの文字コード
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
  AND CHARACTER_SET_NAME IS NOT NULL;

wp-config.php の文字コード設定を確かめる

WordPress は wp-config.php の DB_CHARSET を、データベースへの接続の文字コードとして使います。現在の WordPress の標準は次のとおりです。

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

DB_COLLATE を空にしておくと、WordPress がデータベースの対応状況に合わせて照合順序を選びます。

古いサイトでは DB_CHARSET が書かれていなかったり、別の値になっていたりして、UTF-8 の文字が latin1 の接続で latin1 の列に保存されていることがあります。この状態で DB_CHARSET を utf8mb4 に変えたり、データを別のサーバーに移したりすると、文字化けが表に出ます。設定を元に戻すと表示が直る場合でも、データは latin1 の列に入ったままなので、次の手順でデータを utf8mb4 の列に移してください。

ケース1: 列が utf8(utf8mb3)で、中身が正しい UTF-8

HEX() の結果が E38182… の形で、列の文字コードが utf8 / utf8mb3 なら、そのまま utf8mb4 に変換できます。utf8mb4 は utf8mb3 の上位互換で、絵文字などの4バイト文字も保存できるようになります。

ALTER TABLE wp_posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;

WordPress のテーブルすべて(wp_options、wp_postmeta、wp_comments など)とプラグインが作ったテーブルに対して実行します。テーブルの一覧は SHOW TABLES; で確認できます。

ケース2: 列が latin1 で、中身が UTF-8 のバイト列(二重エンコードの手前)

HEX() の結果が E38182… なのに、列の文字コードが latin1 の場合です。古い WordPress から移行したサイトで多い状態です。

この状態で CONVERT TO CHARACTER SET utf8mb4 を実行してはいけません。MySQL は中身を「latin1 の文字」として utf8mb4 に変換するため、E38182 が C3A3C281E2809A に変わり、文字化けがデータに固定されます。

直し方は2つあります。

方法A: 列をいったんバイナリ型にしてから utf8mb4 にする

バイナリ型(BLOB・VARBINARY)を経由すると、MySQL はバイト列を変換せずにそのまま移します。

-- TEXT 型の列の例
ALTER TABLE wp_posts MODIFY post_title BLOB;
ALTER TABLE wp_posts MODIFY post_title TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;

型は列に合わせて対応させます。

元の型 経由する型
TEXT BLOB
MEDIUMTEXT MEDIUMBLOB
LONGTEXT LONGBLOB
VARCHAR(n) VARBINARY(n)
CHAR(n) BINARY(n)

元の列の定義(NOT NULL、DEFAULT など)は SHOW CREATE TABLE wp_posts; で確認し、2回目の MODIFY で同じ指定を付けてください。付け忘れると、その指定が外れます。列の数が多いので、テーブルの数が多いサイトでは方法Bのほうが手間が少なくなります。

方法B: latin1 のままダンプを取り、utf8mb4 として読み込み直す

--default-character-set=latin1 でダンプを取ると、latin1 の列の中身は変換されずにそのまま(UTF-8 のバイト列のまま)ファイルに書き出されます。そのファイルのテーブル定義を utf8mb4 に書き換えて読み込み直します。

# 1. 変換せずにダンプを取る(SET NAMES を書き出さない)
mysqldump -u ユーザー名 -p --default-character-set=latin1 --skip-set-charset データベース名 > latin1.sql

# 2. テーブル定義の文字コードを utf8mb4 に書き換える
sed -e 's/CHARSET=latin1/CHARSET=utf8mb4/g' \
    -e 's/COLLATE=latin1_swedish_ci/COLLATE=utf8mb4_unicode_520_ci/g' \
    -e 's/CHARACTER SET latin1/CHARACTER SET utf8mb4/g' \
    latin1.sql > utf8mb4.sql

# 3. latin1 の記述が残っていないか確認する
grep -n 'latin1' utf8mb4.sql

# 4. 空のデータベースに utf8mb4 として読み込む
mysql -u ユーザー名 -p --default-character-set=utf8mb4 新しいデータベース名 < utf8mb4.sql

この方法は、データベースのすべてのテーブルが latin1 の場合に使います。すでに utf8mb4 で正しく保存されているテーブルが混ざっていると、手順1でそのテーブルの日本語が latin1 に変換され、? に置き換わります。混在している場合は、mysqldump の後ろにテーブル名を並べて latin1 のテーブルだけをダンプするか、方法Aを使ってください。

新しいデータベースに読み込んだら、wp-config.php の DB_NAME をそちらに切り替え、DB_CHARSET が utf8mb4 になっていることを確認します。

ケース3: すでに二重エンコードされている(C3A3… が保存されている)

HEX() が C3A3C281… の形なら、UTF-8 がもう一度変換された状態で保存されています。過去に誤って CONVERT TO を実行した場合などです。

文字化けする前のバックアップがあれば、そこから復元してケース1またはケース2の手順で直すのが安全です。バックアップが無い場合、列ごとに CONVERT(CAST(CONVERT(列 USING latin1) AS BINARY) USING utf8mb4) で1段戻せることがありますが、cp1252 で表せない文字が混ざると一部が戻らないため、複製した環境で結果を確かめてから適用してください。

-- 戻した結果を確認するだけ(書き換えない)
SELECT ID,
       post_title,
       CONVERT(CAST(CONVERT(post_title USING latin1) AS BINARY) USING utf8mb4) AS fixed
FROM wp_posts
WHERE ID = 123;

移行・復元で文字化けさせないために

  • ダンプを取るときと読み込むときは、どちらも --default-character-set=utf8mb4 を指定する
  • phpMyAdmin でインポートするときは、「ファイルの文字セット」を utf-8 にする
  • ダンプファイルをテキストエディターで開いて保存し直さない(保存形式が Shift_JIS などに変わることがある)
  • 移行先のデータベースも utf8mb4 で作成する
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;

既存のデータベースの標準の文字コードは ALTER DATABASE で変えられますが、これは今後作るテーブルの標準を変えるだけで、既存のテーブルとデータは変わりません。

関連記事

お気軽にご相談ください

お見積りへ お問い合わせへ
▲