1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

メールの文字化けのメカニズムや回避法など

Last updated at Posted at 2025-11-14

今はあまり見なくなりましたが、一昔前は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)
→ 英数字と一部の記号のみ
→ 日本語(マルチバイト文字)は含まれない

つまり、日本語を直接送ることができないのです。

日本語メールの歴史的進化

  1. 初期(〜1990年代): ISO-2022-JP(JIS)エンコーディングで送信

    • 7bit範囲内で日本語を表現できる唯一の方式
    • エスケープシーケンスで文字セットを切り替える
  2. MIME規格の登場(1990年代〜): 非ASCII文字を扱えるように拡張

    • ヘッダ部: MIMEエンコーディング(Base64/Quoted-Printable)
    • ボディ部: Content-Transfer-Encodingで変換
  3. 現代(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の場合:

  1. メールを開く
  2. 「︙」(その他)をクリック
  3. 「メッセージのソースを表示」を選択

確認すべきポイント:

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のサンプルなど

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?