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:表示と設定を確かめる

  1. トップページ、投稿、固定ページ、カテゴリー・タグのページで、日本語が文字化けしていないことを確かめます。
  2. 「外観」→「ウィジェット」やテーマの設定画面で、変換前と同じ設定が残っていることを確かめます。
  3. サイト内検索で日本語の語句を検索し、結果が出ることを確かめます。
  4. 管理画面で新しい投稿を作り、日本語と絵文字を含む題名で保存できることを確かめます。
  5. 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にそろえます。

お気軽にご相談ください

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