2026年7月7日
2026年7月7日
WordPressをShift_JIS・EUC-JPからUTF-8に変換する手順|DB・設定・テーマファイル
古い日本語版のWordPressでは、データベースやテーマファイルが Shift_JIS や EUC-JP で作られていることがあります。現在のWordPressはUTF-8を前提に作られており、新しいサーバーへの移転やPHP・MySQLの更新をきっかけに、文字化けや設定の消失が起きることがあります。
この記事では、データベースを utf8mb4(4バイト文字まで扱えるUTF-8)に変換し、WordPressの設定とテーマファイルをUTF-8にそろえる手順を説明します。SQLの手順は MySQL 8.4 で、シリアライズデータの修復は WordPress 7.1 と WP-CLI で、実際に試して確認しました(2026年11月時点)。
作業の前に知っておくこと
- 元のデータベースは触らず、コピーを変換します。 新しいデータベースを作ってそこに取り込み、変換が終わったら
wp-config.phpの接続先を切り替えます。うまくいかなければ接続先を戻せば元に戻ります。 - シリアライズデータが壊れます。 WordPressは、ウィジェットやテーマ・プラグインの設定を、PHPの
serialize()という形式で保存しています。この形式は文字列の長さをバイト数で記録しているため、日本語1文字が2バイト(Shift_JIS・EUC-JP)から3バイト(UTF-8)に変わると、記録された長さと合わなくなります。直さないと、WordPressはその設定を読めず、ウィジェットや設定が消えたように見えます。手順6で直します。 - Shift_JIS は、Windowsの拡張文字(①、㈱、~ など)を含む CP932(MySQLでは
cp932)として扱うと、文字が欠けにくくなります。EUC-JP の場合は、同じ理由で MySQL ではeucjpms、PHPではeucJP-winを使います。EUC-JP の手順は Shift_JIS と同じ考え方で、文字コードの名前だけを置き換えます(この記事の検証は Shift_JIS のデータで行いました)。
以下の例では、元のデータベースを wp_old、新しいデータベースを wp_utf8、MySQLのユーザーを wpuser とします。-p のあとにパスワードを書くとコマンドの履歴に残るので、-p だけを付けて対話で入力します。
手順1:いまの文字コードを調べる
WordPressの設定
wp-config.php の DB_CHARSET を確かめます。
grep -n "DB_CHARSET\|DB_COLLATE" /var/www/html/wp-config.php
sjis、cp932、ujis、eucjpms などが書かれていれば、WordPressはその文字コードでデータベースと通信しています。行が無い場合は、MySQLの既定の文字コードで通信しています。
WordPress側の文字コードの設定(blog_charset)も確かめます。
wp option get blog_charset
表と列の文字コード
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wp_old' AND CHARACTER_SET_NAME IS NOT NULL
ORDER BY TABLE_NAME, COLUMN_NAME;
実際に入っているバイト列
列の文字コードの宣言と、実際に入っているデータが一致していないことがあります。日本語を含む投稿の題名を16進数で表示して確かめます(123 は日本語の題名を持つ投稿のID)。
SELECT HEX(post_title) FROM wp_old.wp_posts WHERE ID = 123;
たとえば「日」という字は、文字コードごとに次のバイト列になります。
| 文字コード | 「日」のバイト列 | 日本語の部分の見分け方 |
|---|---|---|
| UTF-8 | E697A5 |
3バイトずつ。先頭が E3〜E9 のものが多い |
| Shift_JIS(CP932) | 93FA |
2バイトずつ。先頭が 81〜9F か E0〜FC |
| EUC-JP | C6FC |
2バイトずつ。どちらも A1〜FE(ひらがなは A4 で始まる) |
これで、次のどちらに当たるかを判断します。
| パターン | 列の文字コード | 中身 | 使う手順 |
|---|---|---|---|
| A | cp932・sjis・ujis・eucjpms |
宣言どおりの文字コード | 手順2A |
| B | latin1 |
Shift_JIS や EUC-JP のバイト列がそのまま入っている | 手順2B |
パターンBは、MySQLに文字コードの設定が無かった時代に作られたサイトや、DB_CHARSET を空にしていたサイトで起こります。phpMyAdminで見ると、日本語が “ú–{Œê のような記号の並びで表示されるのが目印です。パターンBの表をそのまま utf8mb4 に変換すると、この記号の並びがUTF-8で保存されてしまい、元に戻せなくなります。
手順2A:宣言どおりの文字コードで入っている場合
元のデータベースを書き出し、新しいデータベースに取り込みます。書き出しファイルの中の表の定義には DEFAULT CHARSET=cp932 などが残るので、取り込んだ時点では元と同じ文字コードの表になります。
mysqldump -u wpuser -p --single-transaction wp_old > wp_old.sql
mysql -u wpuser -p -e "CREATE DATABASE wp_utf8 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;"
mysql -u wpuser -p wp_utf8 < wp_old.sql
wp_old.sql は、そのまま元に戻すためのバックアップにもなります。続けて手順3に進みます。
手順2B:latin1 の列に Shift_JIS・EUC-JP が入っている場合
MySQLに「この列は latin1」と思わせたまま、バイト列を変えずに書き出します。そのうえで書き出しファイルの表の定義を本来の文字コードに書き換えて取り込むと、MySQLが正しい文字として認識します。
# 1. latin1 として、変換せずに書き出す
mysqldump -u wpuser -p --single-transaction --default-character-set=latin1 --skip-set-charset wp_old > wp_old_raw.sql
# 2. 表の定義の latin1 を cp932 に書き換える(EUC-JP なら cp932 を eucjpms に)
sed -e 's/CHARSET=latin1/CHARSET=cp932/g' \
-e 's/ COLLATE=latin1_swedish_ci//g' \
-e 's/CHARACTER SET latin1/CHARACTER SET cp932/g' \
wp_old_raw.sql > wp_old_cp932.sql
# 3. 書き換えた文字コードで取り込む
mysql -u wpuser -p -e "CREATE DATABASE wp_utf8 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;"
mysql -u wpuser -p --default-character-set=cp932 wp_utf8 < wp_old_cp932.sql
sedは、投稿の本文にlatin1という文字列があると、それも書き換えます。書き換える前にgrep -c latin1 wp_old_raw.sqlで件数を見て、表の定義(CREATE TABLEの部分)以外に出てこないかを確かめます。- 照合順序が
latin1_general_ciなど別のものになっている場合は、2行目のlatin1_swedish_ciをその名前に置き換えます。grep -o "COLLATE[= ][a-z0-9_]*" wp_old_raw.sql | sort | uniq -cで一覧できます。
取り込んだら、手順1の HEX() を wp_utf8 に対して実行し、SELECT post_title FROM wp_utf8.wp_posts WHERE ID = 123; で日本語として読めることを確かめてから手順3に進みます。
手順3:すべての表を utf8mb4 に変換する
表ごとの変換文を、データベースの情報から作ります。
mysql -u wpuser -p -N -e "SELECT CONCAT('ALTER TABLE \`', TABLE_NAME, '\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;') FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'wp_utf8' AND TABLE_TYPE = 'BASE TABLE';" > convert.sql
cat convert.sql
mysql -u wpuser -p wp_utf8 < convert.sql
CONVERT TO CHARACTER SET は、列の宣言を変えるだけでなく、中のデータも cp932 などから utf8mb4 に変換します。終わったら、utf8mb4 以外の列が残っていないことを確かめます。
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'wp_utf8' AND CHARACTER_SET_NAME IS NOT NULL AND CHARACTER_SET_NAME <> 'utf8mb4';
結果が0件なら完了です。
変換でエラーになる場合
古いサイトの表は MyISAM という形式のことがあります。utf8mb4 は1文字を最大4バイトで扱うため、索引の付いた長い文字列の列があると Specified key was too long というエラーになることがあります。その表は先に InnoDB に変えてから変換します。
ALTER TABLE `wp_postmeta` ENGINE=InnoDB;
手順4:wp-config.php を新しいデータベースに向ける
wp-config.php を、念のためコピーしてから編集します。
<?php
// 変更する行だけを抜き出した例
define( 'DB_NAME', 'wp_utf8' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );
DB_CHARSET を古い値(sjis など)のままにすると、WordPressはUTF-8の表からShift_JISでデータを受け取ることになり、画面が文字化けします。DB_NAME と DB_CHARSET は同時に変えます。
手順5:WordPressの文字コード設定を UTF-8 にする
wp option update blog_charset UTF-8
WP-CLI が使えない場合は、管理画面の「設定」→「表示設定」で変更できます。WordPressは、blog_charset がUTF-8以外のときだけ、この画面に「ページとフィードの文字コード」の欄を表示します(UTF-8にすると欄は消えます)。
手順6:壊れたシリアライズデータを直す
手順3の変換のあと、日本語を含むシリアライズデータは、記録された長さと実際のバイト数が合わなくなっています。手元の検証では、a:1:{s:5:"title";s:6:"日本語";} のように、UTF-8では9バイトの「日本語」に s:6 という長さが残り、wp option get で読もうとすると「その設定は存在しない」という扱いになりました。
次のスクリプトは、壊れているデータだけを選び、いったん元の文字コードのバイト列に戻してから読み込み、UTF-8に変換して保存し直します。対象は wp_options、wp_postmeta、wp_usermeta、wp_termmeta、wp_commentmeta です。fix-serialized-charset.php という名前で、WordPressのフォルダーの外などに保存します。
<?php
/**
* 文字コード変換で長さが合わなくなったシリアライズデータを直す(WP-CLI 用)。
*
* 使い方:
* wp eval-file fix-serialized-charset.php CP932 確認だけ(書き換えない)
* wp eval-file fix-serialized-charset.php CP932 apply 書き換える
* 元の文字コードは Shift_JIS 系なら CP932、EUC-JP 系なら eucJP-win を指定する。
*/
global $wpdb;
$from = isset( $args[0] ) ? $args[0] : 'CP932';
$apply = isset( $args[1] ) && 'apply' === $args[1];
$targets = array(
array( $wpdb->options, 'option_id', 'option_value' ),
array( $wpdb->postmeta, 'meta_id', 'meta_value' ),
array( $wpdb->usermeta, 'umeta_id', 'meta_value' ),
array( $wpdb->termmeta, 'meta_id', 'meta_value' ),
array( $wpdb->commentmeta, 'meta_id', 'meta_value' ),
);
// 配列の中の文字列(キーも含む)を UTF-8 に変換する。
function b079_to_utf8( $data, $from ) {
if ( is_string( $data ) ) {
return mb_convert_encoding( $data, 'UTF-8', $from );
}
if ( is_array( $data ) ) {
$out = array();
foreach ( $data as $key => $value ) {
$new_key = is_string( $key ) ? mb_convert_encoding( $key, 'UTF-8', $from ) : $key;
$out[ $new_key ] = b079_to_utf8( $value, $from );
}
return $out;
}
return $data;
}
// オブジェクトを含むデータはこのスクリプトでは扱わない。
function b079_has_object( $data ) {
if ( is_object( $data ) ) {
return true;
}
if ( is_array( $data ) ) {
foreach ( $data as $value ) {
if ( b079_has_object( $value ) ) {
return true;
}
}
}
return false;
}
$fixed = 0;
$ng = 0;
foreach ( $targets as $target ) {
list( $table, $id_col, $val_col ) = $target;
$rows = $wpdb->get_results( "SELECT `$id_col` AS id, `$val_col` AS v FROM `$table` WHERE `$val_col` LIKE 'a:%' OR `$val_col` LIKE 's:%' OR `$val_col` LIKE 'O:%'" );
foreach ( $rows as $row ) {
if ( ! is_serialized( $row->v ) || false !== @unserialize( $row->v, array( 'allowed_classes' => false ) ) ) {
continue; // 壊れていない
}
// 一度元の文字コードのバイト列に戻すと、記録された長さと一致する。
$original = mb_convert_encoding( $row->v, $from, 'UTF-8' );
$data = @unserialize( $original, array( 'allowed_classes' => false ) );
if ( false === $data || b079_has_object( $data ) ) {
echo "NG $table $id_col={$row->id}(手作業で確認)\n";
++$ng;
continue;
}
$new = serialize( b079_to_utf8( $data, $from ) );
echo ( $apply ? 'FIX ' : 'TODO ' ) . "$table $id_col={$row->id}\n";
if ( $apply ) {
$wpdb->update( $table, array( $val_col => $new ), array( $id_col => $row->id ) );
}
++$fixed;
}
}
echo "対象 $fixed 件 / 要確認 $ng 件" . ( $apply ? '(書き換え済み)' : '(確認のみ)' ) . "\n";
手順4・5を終えて、WordPressが wp_utf8 につながった状態で実行します。
cd /var/www/html
# まず確認だけ(何件直すかが表示される)
wp eval-file /path/to/fix-serialized-charset.php CP932
# 問題なければ書き換え、キャッシュを消す
wp eval-file /path/to/fix-serialized-charset.php CP932 apply
wp cache flush
# もう一度実行して「対象 0 件」になることを確かめる
wp eval-file /path/to/fix-serialized-charset.php CP932
- EUC-JP のサイトでは
CP932をeucJP-winに置き換えます。 NGと表示された行は、オブジェクトを含むデータか、元の文字コードに戻しても読めないデータです。多くはプラグインの一時データ(_transient_で始まる設定など)で、消しても作り直されます。それ以外は、該当のプラグインの設定画面で設定し直します。- マルチサイトでは、サイトごとに表が分かれているので、
--url=でサイトを指定してサイトごとに実行します。
手順7:テーマ・プラグインのファイルを UTF-8 にする
自作のテーマや古いプラグインのファイルが Shift_JIS のままだと、画面の一部だけが文字化けします。UTF-8として正しくないファイルを探します。
cd /var/www/html/wp-content/themes/mytheme
find . -type f \( -name "*.php" -o -name "*.css" -o -name "*.js" -o -name "*.html" \) \
-exec sh -c 'iconv -f UTF-8 -t UTF-8 "$1" > /dev/null 2>&1 || echo "$1"' _ {} \;
表示されたファイルを、バックアップを取ってから変換します。
cp header.php header.php.bak
iconv -f CP932 -t UTF-8 header.php.bak > header.php
(EUC-JP のファイルは -f EUC-JP-MS にします。)ファイルの中に <meta charset="Shift_JIS"> や charset=EUC-JP のように文字コードを直接書いている箇所があれば、<meta charset="<?php bloginfo( 'charset' ); ?>"> のようにWordPressの設定から出力する形に直します。
公開されているプラグインやテーマは、UTF-8に対応した最新版に更新するのが基本です。更新が止まっていてShift_JISのファイルしかないものは、代わりのプラグインを検討します。
手順8:表示と設定を確かめる
- トップページ、投稿、固定ページ、カテゴリー・タグのページで、日本語が文字化けしていないことを確かめます。
- 「外観」→「ウィジェット」やテーマの設定画面で、変換前と同じ設定が残っていることを確かめます。
- サイト内検索で日本語の語句を検索し、結果が出ることを確かめます。
- 管理画面で新しい投稿を作り、日本語と絵文字を含む題名で保存できることを確かめます。
- RSSフィード(
/feed/)を開き、文字化けしていないことを確かめます。
問題があった場合は、wp-config.php の DB_NAME と DB_CHARSET を元に戻すと、変換前のデータベースで動く状態に戻ります(手順5・6の変更は新しいデータベースにだけ入っています)。
まとめ
- 変換は元のデータベースのコピーで行い、
wp-config.phpの接続先を切り替えます。 - 列の文字コードと中身が一致しているか(パターンA)、latin1 の列にShift_JIS・EUC-JPが入っているか(パターンB)を
HEX()で見分け、パターンBは書き出しファイルの文字コードの宣言を直してから取り込みます。 ALTER TABLE … CONVERT TO CHARACTER SET utf8mb4で表を変換し、DB_CHARSETをutf8mb4、blog_charsetをUTF-8にします。- 日本語を含むシリアライズデータは長さが合わなくなるので、元の文字コードに戻して読み込み、UTF-8で保存し直します。
- テーマやプラグインのファイルも
iconvでUTF-8にそろえます。