2026年5月28日

2026年5月28日

WordPressのwp_mail()でHTMLメールが文字化けする場合の原因と対処法

wp_mail() で送ったHTMLメールが文字化けする場合、多くは「Content-Type と文字コード(charset)の指定」「本文の転送エンコーディング」「件名に残ったHTMLエンティティ」のどれかが原因です。受信したメールのソース(ヘッダー)を見れば、どれに当たるかをかなり絞り込めます。この記事では、症状の読み取り方から、可能性の高い原因の順に確認方法と直し方を説明します。

コードは子テーマの functions.php か、独自プラグイン・mu-plugin に書く前提です。メール送信の設定を変えると、問い合わせフォームや会員登録の通知など、サイト内のすべてのメールに影響することがあります。変更前にバックアップを取り、ステージング環境やテスト用の受信アドレスで確認してから本番に反映してください。

まず症状を見分ける

「文字化け」と一口に言っても、見え方で原因の見当がつきます。

  • **本文に <p> や <br> などのタグがそのまま表示される**:文字化けではなく、メールがテキスト形式で送られています。Content-Type の指定が抜けています。
  • **本文の日本語が 縺ゅ・ や ã‚ のような記号の列になる**:送信側と受信側で文字コードの解釈が食い違っています。charset の指定かファイルの保存形式を疑います。
  • **長い本文の途中だけが崩れる、タグの途中に ! やスペースが入る**:1行が長すぎて途中の経路で折り返された可能性があります。転送エンコーディングの問題です。
  • **件名の &#8211; や &amp; がそのまま見える**:文字化けではなく、投稿タイトルなどのHTMLエンティティが件名に残っています。
  • **件名だけが =?UTF-8?B?... のまま表示される**:件名を自分でエンコードして二重になっている可能性があります。

受信したメールのソースを確認する

原因を確かめるには、受信したメールの「ソース」「原文」を表示する機能を使います(Gmail などのWebメールにもあり、名称はサービスやバージョンで異なります)。見るべき行は次の3つです。

Subject: =?UTF-8?B?44GK5ZWP44GE5ZCI44KP44Gb44KS5Y+X44GR5LuY44GR44G+44GX44Gf?=
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit

Content-Type が text/plain ならHTMLとして送られていません。charset が UTF-8 以外(iso-8859-1 など)なら文字コードの指定に問題があります。Content-Transfer-Encoding は本文の運び方で、8bit は本文をそのまま流す方式、base64 や quoted-printable は7bitの文字だけに変換して運ぶ方式です。

開発中は、実際の宛先に送らずにメールを受け止めて中身を表示できるツール(Mailpit など)をローカル環境で使うと、ヘッダーと本文を何度でも確認できます。

原因1:Content-Type が text/html になっていない

wp_mail() は、指定が無ければ text/plain でメールを送ります。HTMLとして送るには、ヘッダーに Content-Type を渡します。charset もここで一緒に指定できます。

<?php
function mytheme_send_html_mail( $to, $subject, $html ) {
	$headers = array( 'Content-Type: text/html; charset=UTF-8' );
	return wp_mail( $to, $subject, $html, $headers );
}

wp_mail_content_type フィルターで text/html を返す方法もよく紹介されますが、フィルターを付けたままにすると、パスワードリセットなどWordPress本体やほかのプラグインが送るテキストメールまでHTML扱いになり、改行が消えたりリンクが崩れたりします。フィルターを使う場合は、送信の直前に追加して直後に remove_filter() で外してください。上のように送信ごとにヘッダーで指定するほうが、影響範囲を限定できます。

原因2:charset の指定と実際の文字コードが合っていない

WordPress は、ヘッダーで charset が指定されなければサイトの文字コード(オプション blog_charset、通常は UTF-8)を使います。次の点を順に確認します。

サイトの文字コードWP-CLI が使えるなら wp option get blog_charset で確認できます。古いサイトを移行した場合などに UTF-8 以外になっていることがあります。値を変えると既存のコンテンツの表示にも影響するため、変更する場合はデータベースのバックアップを取り、ステージングで確かめてからにしてください。

メールのテンプレートファイルの保存形式HTMLのテンプレートを別ファイルにしている場合、そのファイルが Shift_JIS などで保存されていると、charset を UTF-8 と宣言していても中身と食い違って化けます。エディターでファイルの文字コードを UTF-8 にして保存し直します。PHPファイルの場合は BOM なしの UTF-8 にします。BOM 付きだと、出力の先頭に余分な文字が入る原因にもなります。

**wp_mail_charset フィルターを使うプラグイン**メールの文字コードを ISO-2022-JP などに変えるプラグインやコードが入っていると、HTML本文と組み合わせたときに化けることがあります。古い携帯端末向けの対策として入れられたまま残っている例があるため、wp_mail_charset や phpmailer_init を使っているコードを検索して確認します。現在の主要なメールソフトは UTF-8 を扱えるので、特別な事情が無ければ UTF-8 にそろえるのが無難です。

HTML内の meta 指定メールソフトは主にメールヘッダーの charset を見ますが、HTML本文の <head> にも <meta charset="UTF-8"> を入れておくと、一部の環境での食い違いを減らせます。

原因3:本文の1行が長すぎて途中で折り返されている

メールの規格では1行の長さに上限があり、HTMLを圧縮して改行の無い1行にしていると、上限を超えます。途中のメールサーバーが勝手に改行を入れると、日本語の文字の途中で切れて化けたり、タグの途中に余計な文字が入ったりします。WordPress が使っている PHPMailer には、長すぎる行を検出したときに転送エンコーディングを切り替える処理がありますが、SMTP プラグインや外部の送信サービスを経由するとその前提が変わることもあります。

対策は2つあります。1つは、HTMLテンプレートを適度に改行しておくことです。もう1つは、HTMLメールのときだけ転送エンコーディングを base64 にすることです。phpmailer_init アクションで、送信直前の PHPMailer オブジェクトを調整できます。

<?php
add_action( 'phpmailer_init', function ( $phpmailer ) {
	if ( 'text/html' === $phpmailer->ContentType ) {
		$phpmailer->CharSet  = 'UTF-8';
		$phpmailer->Encoding = 'base64';
	} else {
		// 同じオブジェクトが次の送信でも使われるため、テキストメールでは既定に戻す
		$phpmailer->Encoding = '8bit';
	}
} );

wp_mail() は1回のリクエスト内で同じ PHPMailer オブジェクトを使い回します。HTMLのときだけ設定を変えて元に戻さないと、後に続くテキストメールにも設定が残るため、上の例では両方の場合を書いています。base64 にすると本文のサイズは少し大きくなりますが、途中の経路での折り返しの影響は受けにくくなります。

原因4:件名にHTMLエンティティが残っている

get_the_title() で取得した投稿タイトルは、表示用に記号が &#8211; や &#8217; のような文字参照に変換されていることがあります。HTMLとして表示される本文では問題になりませんが、件名はテキストとして扱われるため、そのまま見えてしまいます。件名に使う前にデコードします。

<?php
$post_title = wp_specialchars_decode( get_the_title( $post_id ), ENT_QUOTES );
$subject    = sprintf( '【新着】%s', $post_title );

同じ理由で、サイト名を件名に入れる場合も wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ) のようにデコードしてから使います。WordPress 本体の通知メールもこの形でサイト名を扱っています。

原因5:件名や本文を自分でエンコードしている

日本語のメール送信について調べると、mb_encode_mimeheader() で件名をエンコードする例や、mb_convert_encoding() で本文を変換する例が出てきます。しかし wp_mail() は内部の PHPMailer が件名のエンコードを行うため、自分でもエンコードすると二重になり、件名が =?UTF-8?B? で始まる文字列のまま表示されます。wp_mail() には UTF-8 の文字列をそのまま渡してください。

原因6:SMTP プラグインや送信サービス側の設定

WP Mail SMTP や FluentSMTP などの SMTP プラグイン、または外部の送信サービスの API を使って送る設定にしている場合、実際の送信はプラグイン側の処理で行われることがあります。その場合、上の phpmailer_init の調整が効かないことがあります(送信方式によって異なります)。一時的に SMTP プラグインを無効にして化けが直るかを確かめれば、原因がプラグイン側にあるかを切り分けられます。プラグインの設定に文字コードやエンコーディングに関する項目があるか、各プラグインのドキュメントで確認してください(2026年10月現在)。

確認と送信失敗の記録

修正後は、Gmail、Outlook、スマートフォンのメールアプリなど、読者が使う代表的な環境で受信して確認します。同じHTMLでもメールソフトによって表示が異なるためです。

送信そのものが失敗していないかは、wp_mail_failed アクションで記録できます。

<?php
add_action( 'wp_mail_failed', function ( $error ) {
	if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
		error_log( 'wp_mail failed: ' . $error->get_error_message() );
	}
} );

ログには宛先や本文などの個人情報を書き出さず、エラーメッセージだけにとどめます。調査が終わったら、デバッグログの設定も元に戻してください。

まとめ

  • タグがそのまま見えるならテキスト形式で送られています。Content-Type: text/html; charset=UTF-8 を送信ごとのヘッダーで指定します。
  • 日本語が記号の列になるなら、blog_charset、テンプレートファイルの保存形式、wp_mail_charset を使うコードを順に確認し、UTF-8 にそろえます。
  • 長い行の途中だけが崩れるなら、HTMLを改行するか、phpmailer_init でHTMLメールの転送エンコーディングを base64 にします。
  • 件名の &#8211; などは wp_specialchars_decode() でデコードし、件名を自分でエンコードしないようにします。
  • 受信メールのソースでヘッダーを確認すると、原因をすばやく絞り込めます。

お気軽にご相談ください

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