今はあまり見なくなりましたが、一昔前はGmailでさえ文字化けがよく起こってましたね。
メールはインターネット初期から使われているシステムだけに仕組みが複雑です。
今回は、メールの文字化けが起きる根本的なメカニズムと、回避法について。
例によってPHPで書いてますが、他の言語でも考え方は通用すると思います。
メールの基本構造を理解する
文字化け対策の前に、まずメールがどのような構造になっているかを理解しましょう。
メールメッセージの大まかな構造
メールメッセージの形式はRFC 5322で規定されており、大きくヘッダ部とボディ部の2つに分かれます。
ヘッダ部
────────────────
From: sender@example.com
To: recipient@example.com
Subject: 件名
Date: Mon, 10 Nov 2025 12:00:00 +0900
Message-ID: <unique-id@example.com>
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64
────────────────
(空行)
────────────────
ボディ部(本文)
────────────────
メールの本文がここに入ります。
この2つの部分は別々に文字化け対策が必要です。実は、ヘッダ部の方が文字化けしやすい傾向があります。
なぜメールは文字化けするのか - 歴史的背景
元凶は「7bit制約」
メールの根幹となるSMTP(Simple Mail Transfer Protocol)は、もともと7bitのASCII文字のみを扱う設計でした。
7bit ASCII文字の範囲: 0x00 〜 0x7F (0〜127)
→ 英数字と一部の記号のみ
→ 日本語(マルチバイト文字)は含まれない
つまり、日本語を直接送ることができないのです。
日本語メールの歴史的進化
-
初期(〜1990年代): ISO-2022-JP(JIS)エンコーディングで送信
- 7bit範囲内で日本語を表現できる唯一の方式
- エスケープシーケンスで文字セットを切り替える
-
MIME規格の登場(1990年代〜): 非ASCII文字を扱えるように拡張
- ヘッダ部: MIMEエンコーディング(Base64/Quoted-Printable)
- ボディ部: Content-Transfer-Encodingで変換
-
現代(2000年代〜): UTF-8が主流に
- しかし古いメールシステムとの互換性問題が残る
- 今でもISO-2022-JPが「最も安全」な選択肢
ヘッダ部の文字化け対策
ヘッダ部にはFrom(差出人)、Subject(件名)などの重要な情報が含まれます。ここが文字化けすると、受信者に非常に悪い印象を与えます。
ヘッダ部の主要要素
From: 送信者名 <sender@example.com>
Reply-To: 返信先アドレス
To: 宛先
Cc: カーボンコピー
Bcc: ブラインドカーボンコピー
Subject: 件名
Date: 送信日時
Message-ID: メッセージの一意識別子
In-Reply-To: 返信元メッセージID
References: 返信履歴のメッセージID
Content-Type: コンテンツタイプとcharset
Content-Transfer-Encoding: 本文の変換方式
重要なポイント: これらのヘッダも元々は7bit ASCII文字しか使えません。日本語を含める場合は特別な処理が必要です。
ヘッダの日本語対応 - MIMEエンコーディング
ヘッダに日本語を入れるには、MIMEエンコーディングという特殊な形式に変換する必要があります。
変換の流れ:
元の文字列: "日本語サブジェクト"
↓
1. UTF-8 → ISO-2022-JP(JIS)に変換
↓
2. Base64エンコーディング
↓
3. =?charset?encoding?encoded-text?= の形式で囲む
↓
結果: =?ISO-2022-JP?B?GyRCRnxLXDhsJUUlayVjJTshPCVlGyhC?=
PHPでの実装例
方法1: 手動でエンコーディング
<?php
// UTF-8で書かれたPHPファイルであることを明示
mb_internal_encoding("UTF-8");
$original_subject = "日本語サブジェクト";
// UTF-8 → ISO-2022-JP(JIS)に変換
$jis_subject = mb_convert_encoding($original_subject, "JIS", "UTF-8");
// Base64エンコード
$base64_subject = base64_encode($jis_subject);
// MIMEヘッダ形式に整形
$subject = "=?ISO-2022-JP?B?" . $base64_subject . "?=";
// 結果: =?ISO-2022-JP?B?GyRCRnxLXDhsJUUlayVjJTshPCVlGyhC?=
方法2: mb_encode_mimeheader関数を使う(推奨)
<?php
mb_internal_encoding("UTF-8");
// この1行で上記の処理をすべて実行
$subject = mb_encode_mimeheader(
'日本語サブジェクト', // 元の文字列
'ISO-2022-JP', // 出力文字コード
'B', // エンコーディング方式(B=Base64, Q=Quoted-Printable)
"\n" // 改行文字
);
推奨理由:
- コードが簡潔
- 長い文字列の自動折り返しに対応
- エラーが起きにくい
From(差出人名)の設定例
<?php
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
// 差出人名に日本語を使う場合
$from_name = mb_encode_mimeheader('山田太郎', 'ISO-2022-JP', 'B');
$mail->setFrom('sender@example.com', $from_name);
// 件名
$subject = mb_encode_mimeheader('重要なお知らせ', 'ISO-2022-JP', 'B');
$mail->Subject = $subject;
実際のメールヘッダでの見え方:
From: =?ISO-2022-JP?B?GyRCLzNFREBiOmEbKEI=?= <sender@example.com>
Subject: =?ISO-2022-JP?B?GyRCPCwlZiVrJUo0fCQ5JE4bKEI=?=
受信者のメールクライアントが自動的にデコードして「山田太郎」「重要なお知らせ」と表示します。
ヘッダエンコーディングの落とし穴
❌ よくある間違い: UTF-8をそのまま使う
// ❌ 間違い(古い環境で文字化けする)
$subject = '日本語サブジェクト';
// ⭕ 正しい
$subject = mb_encode_mimeheader('日本語サブジェクト', 'ISO-2022-JP', 'B');
ボディ部(本文)の文字化け対策
本文の文字化け対策は、ヘッダ部とは異なるアプローチが必要です。
Content-Typeとcharsetの指定
本文の文字コードはContent-Typeヘッダで指定します。
Content-Type: text/plain; charset=ISO-2022-JP
または
Content-Type: text/plain; charset=UTF-8
ISO-2022-JP vs UTF-8 - どちらを使うべきか?
現代のメール送信では、2つの選択肢があります。
選択肢1: ISO-2022-JP
メリット:
- ほぼすべての日本語メール環境で正しく表示される
- ガラケーなど古い端末でも文字化けしない
- 企業の古いメールシステムでも安全
デメリット:
- 使える文字が限定的(JIS第1・第2水準漢字のみ)
- 絵文字、特殊記号(㈱、①、🎉など)が使えない
- 半角カナも使えない
設定例:
<?php
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
// ヘッダ
$mail->Subject = mb_encode_mimeheader('件名', 'ISO-2022-JP', 'B');
// 本文をISO-2022-JPに変換
$body = "メールの本文です。\nよろしくお願いします。";
$mail->Body = mb_convert_encoding($body, 'JIS', 'UTF-8');
// Content-Typeの設定
$mail->CharSet = 'ISO-2022-JP';
$mail->Encoding = '7bit'; // ISO-2022-JPは7bit
選択肢2: UTF-8(現代的な選択)
メリット:
- すべての文字が使える(絵文字、特殊記号も可)
- 世界中の言語に対応
- 現代的なシステムなら問題なく表示
デメリット:
- 古いメールシステムで文字化けする可能性
- ガラケーでは表示できない
設定例:
<?php
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
// ヘッダ(UTF-8の場合もISO-2022-JPにするのが安全)
$mail->Subject = mb_encode_mimeheader('件名', 'ISO-2022-JP', 'B');
// 本文はUTF-8のまま
$mail->Body = "メールの本文です。\n絵文字も使えます🎉";
// Content-Typeの設定
$mail->CharSet = 'UTF-8';
$mail->Encoding = 'base64'; // UTF-8はbase64または8bit
Content-Transfer-Encodingの選択
Content-Transfer-Encodingは、本文をどのように変換して送信するかを指定します。
ISO-2022-JPの場合: 7bit
$mail->Encoding = '7bit';
ISO-2022-JPは元々7bit範囲内に収まるように設計されているため、7bitを指定します。
UTF-8の場合: base64または8bit
// 推奨: base64
$mail->Encoding = 'base64';
// または
$mail->Encoding = '8bit';
base64の特徴:
- バイナリデータをテキストに変換
- すべての環境で確実に動作
- データサイズが約33%増加
8bitの特徴:
- 変換なしでそのまま送信
- データサイズが増えない
- 一部の古いメールサーバーで問題が起きる可能性
結論: 迷ったらbase64を使いましょう。
実際のメール本文の見え方
ISO-2022-JP + 7bitの場合
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit
$B%a!<%k$NK\J8$G$9!#(B
$B$h$m$7$/$*4j$$$$?$7$^$9!#(B
エスケープシーケンスで文字セットを切り替えています($Bで日本語開始、(Bで英数字に戻る)。
UTF-8 + base64の場合
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64
44Oh44O844Or44Gu5pys5paH44Gn44GZ44CCCuOCiOOCjeOBleurlOOBiumihumhhOOBhO
OBl+OBvuOBmeOAggo=
Base64でエンコードされた状態で送信されます。
実際のメール送信サンプル
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
// このPHPファイルはUTF-8(BOMなし)で保存すること
mb_internal_encoding("UTF-8");
function send_safe_email($to, $subject, $body, $from_email, $from_name) {
$mail = new PHPMailer(true);
try {
// SMTP設定
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = 'your-email@gmail.com';
$mail->Password = 'your-app-password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// ========================================
// 文字化け対策の設定(UTF-8)
// ========================================
// UTF-8で送信する場合
$mail->CharSet = 'UTF-8';
$mail->Encoding = 'base64';
// ヘッダは念のためISO-2022-JPに
$encoded_subject = mb_encode_mimeheader($subject, 'ISO-2022-JP', 'B');
$encoded_from_name = mb_encode_mimeheader($from_name, 'ISO-2022-JP', 'B');
// 本文はUTF-8のまま
$mail->Body = $body;
// 送信者・受信者設定
$mail->setFrom($from_email, $encoded_from_name);
$mail->addAddress($to);
// 件名
$mail->Subject = $encoded_subject;
// 送信
$mail->send();
return true;
} catch (Exception $e) {
error_log("メール送信エラー: " . $mail->ErrorInfo);
return false;
}
}
HTMLメールの文字化け対策
テキストメールだけでなく、HTMLメールでも文字化け対策が必要です。
<?php
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
// HTML形式を有効化
$mail->isHTML(true);
// 文字コード設定
$mail->CharSet = 'UTF-8';
$mail->Encoding = 'base64';
// 件名
$mail->Subject = mb_encode_mimeheader('HTMLメールの件名', 'ISO-2022-JP', 'B');
// HTMLメール本文
$html_body = '
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HTMLメール</title>
</head>
<body>
<h1>こんにちは</h1>
<p>HTMLメールの本文です。</p>
</body>
</html>
';
$mail->Body = $html_body;
// テキスト版も用意(HTMLメールが表示できない環境用)
$mail->AltBody = "こんにちは\n\nHTMLメールの本文です。";
重要: HTMLメール内の<meta charset="UTF-8">と、PHPMailerのCharSet設定を一致させてください。
デバッグ方法
文字化けが起きた場合のデバッグ方法です。
1. メールのソースを確認
Gmailの場合:
- メールを開く
- 「︙」(その他)をクリック
- 「メッセージのソースを表示」を選択
確認すべきポイント:
Content-Type: text/plain; charset=ISO-2022-JP ← charsetが正しいか
Content-Transfer-Encoding: 7bit ← エンコーディングが適切か
Subject: =?ISO-2022-JP?B?...?= ← MIMEエンコードされているか
(本文)
実際の本文がどう表示されているか
2. PHPMailerのデバッグモード
<?php
// デバッグ出力を有効化
$mail->SMTPDebug = 2; // 0=なし, 1=クライアント, 2=クライアント+サーバー
$mail->Debugoutput = 'html'; // または 'echo'
3. 送信前に内容を確認
<?php
// 送信前に変換結果を確認
echo "件名: " . bin2hex($mail->Subject) . "\n";
echo "本文: " . bin2hex($mail->Body) . "\n";
推奨設定(最も安全)
// ヘッダ: ISO-2022-JP
$mail->Subject = mb_encode_mimeheader($subject, 'ISO-2022-JP', 'B');
// 本文: ISO-2022-JP + 7bit
$mail->CharSet = 'ISO-2022-JP';
$mail->Encoding = '7bit';
$mail->Body = mb_convert_encoding($body, 'JIS', 'UTF-8');
現代的な設定(絵文字を使う場合)
// ヘッダ: ISO-2022-JP(互換性のため)
$mail->Subject = mb_encode_mimeheader($subject, 'ISO-2022-JP', 'B');
// 本文: UTF-8 + base64
$mail->CharSet = 'UTF-8';
$mail->Encoding = 'base64';
$mail->Body = $body; // UTF-8のまま
メールの文字化けは複雑ですが、基本を理解して正しく設定すれば確率を減らせます。特にヘッダと本文を分けて考えること、ISO-2022-JPの制約を理解することが重要です。
GitHubのサンプルなど