RFCとは
RFC(Request for Comments)は、インターネット技術の仕様をまとめた文書群です。HTTP、TCP/IP、メールなど、インターネットの根幹をなす技術の多くがRFCとして公開されています。
もともとは技術者同士が意見交換するための文書でしたが、現在ではインターネット標準を定義する正式な仕様書として機能しています。
メール関連で特に重要なRFCは以下の通りです。
| RFC | 内容 |
|---|---|
| RFC 5322 | メールフォーマット |
| RFC 2045〜2049 | MIME仕様 |
| RFC 2047 | メールヘッダーの文字エンコード |
| RFC 2616 | HTTP/1.1(現在は廃止) |
本記事で解説するRFC 2047は、メールヘッダー内で日本語などの非ASCII文字を扱うための仕様です。
RFC 2047が必要になった背景
初期の電子メールはASCII文字のみを扱う前提で設計されていました。ASCIIで表現できるのは英数字、記号、一部の制御文字に限られます。
つまり、以下のような日本語の件名はそのままメールヘッダーに記載できませんでした。
お問い合わせありがとうございます
現在では何の変哲もない件名ですが、メールの仕様上、ヘッダーにはASCII文字しか書けないというルールがあったのです。
ASCII制約の具体的な問題
メールヘッダーのSubjectフィールドは、RFC 5322によって7ビットASCIIの範囲内で記述することが求められています。日本語のようなマルチバイト文字をそのまま入れると、中継するメールサーバーが正しく処理できず、文字化けや配送エラーの原因になります。
この問題を解決するために登場したのがRFC 2047です。RFC 2047は、ASCII以外の文字列をASCIIのみの表現に変換して送信するための仕組みを定義しています。
RFC 2047の形式
RFC 2047では、以下のフォーマットを使って非ASCII文字を表現します。
=?charset?encoding?encoded-text?=
各要素の意味は以下の通りです。
| 項目 | 説明 |
|---|---|
charset | 文字コード(例:UTF-8、ISO-2022-JP) |
encoding | エンコード方式(BまたはQ) |
encoded-text | 変換後の文字列 |
実際の例を見てみます。
=?UTF-8?B?44GT44KT44Gr44Gh44Gv?=
この文字列は受信側に対して、「UTF-8で符号化され、Base64でエンコードされたデータである」ことを伝えています。受信側はこの情報をもとに正しくデコードし、元の日本語テキストを復元できます。
RFC 2047の構造を図で理解する
RFC 2047は、文字コードとエンコード方式を明示することで、受信側が正しく元のテキストを復元できる仕組みになっています。
エンコード方式:BとQ
RFC 2047のencodingパートには、以下の2種類が利用されます。
| 記号 | 意味 | 特徴 |
|---|---|---|
B | Base64 | 任意のバイト列を効率的にASCII化できる。日本語で主流 |
Q | Quoted-Printable | ASCII文字が多い場合に効率的。欧文で主流 |
日本語のメールではBase64(B)が使われるケースが圧倒的に多いです。日本語はASCII外の文字がほとんどを占めるため、Quoted-Printableだと逆に冗長になってしまうためです。
エンコードとデコードの基本
エンコードとは、ある形式のデータを別の形式に変換することです。逆の操作がデコードです。
| 用語 | 意味 |
|---|---|
| エンコード(符号化) | データを別の形式に変換する |
| デコード(復号) | 元の形式に戻す |
RFC 2047におけるBase64エンコードの場合、変換は可逆的です。
こんにちは → 44GT44KT44Gr44Gh44Gv → こんにちは
情報そのものは失われず、表現形式が変わっているだけです。
Base64エンコードの処理フロー
RFC 2047でBase64を利用する場合、内部的には以下の段階を経ています。
- 日本語文字列をUTF-8のバイト列に変換する
- バイト列をBase64でエンコードする
- RFC 2047の形式に組み立てる
ここで重要なのは、Base64は文字コードではないという点です。UTF-8が文字コード、Base64はあくまで変換方式(エンコーディング)です。この区別を混同すると、デコード時に誤った手順を踏んでしまう原因になります。
Node.jsでRFC 2047形式を作る
Node.jsではBufferを使うことで、RFC 2047形式の文字列を簡潔に生成できます。
const subject = 'お問い合わせありがとうございます';
const encodedSubject =
`=?UTF-8?B?${Buffer.from(subject).toString('base64')}?=`;
console.log(encodedSubject);
このコードの中で起きていることを順に見ていきます。
Buffer.from() による文字列のバイト列変換
Buffer.from()は文字列をバイト列に変換します。第2引数を省略した場合、デフォルトでUTF-8として処理されます。
const buf = Buffer.from('こんにちは');
// <Buffer e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 81 af>
BufferはNode.jsが提供するバイナリデータのコンテナです。文字列をバイト列として扱うための中間表現だと考えてください。
toString(‘base64’) によるBase64変換
BufferのtoString('base64')を呼ぶと、バイト列がBase64文字列に変換されます。
Buffer.from('こんにちは').toString('base64');
// '44GT44KT44Gr44Gh44Gv'
この結果はASCII文字のみで構成されているため、メールヘッダーに安全に埋め込めます。
RFC 2047形式への組み立て
テンプレートリテラルで=?UTF-8?B?と?=で囲めば完成です。
const text = 'こんにちは';
const encoded = `=?UTF-8?B?${Buffer.from(text).toString('base64')}?=`;
// =?UTF-8?B?44GT44KT44Gr44Gh44Gv?=
受信側のメールクライアントはこのフォーマットを解析し、UTF-8 + Base64でデコードして元の日本語テキストを復元します。
RFC 2047のラベルが必要な理由
仮にBase64文字列だけを送信した場合を考えてみます。
44GT44KT44Gr44Gh44Gv
受信側はこの文字列を見ただけでは、以下の情報が判断できません。
- 文字コードがUTF-8なのかShift_JISなのか
- エンコード方式がBase64なのかQuoted-Printableなのか
文字コードを間違えれば文字化けし、エンコード方式を間違えればデコード自体が失敗します。
RFC 2047のフォーマットは、変換済みデータと一緒に「どう変換したか」のメタ情報を送ることで、受信側に正しいデコード手順を伝えています。つまりRFC 2047は単なるBase64の仕様ではなく、「受信側にデコード方法を伝えるためのラベル付きフォーマット」です。
実務で遭遇しやすい注意点
エンコード済み文字列の長さ制限
RFC 2047では、エンコードされた1つの「encoded-word」は75文字以内に収める必要があります。長い件名の場合は複数のencoded-wordに分割し、それぞれを折り返して連結します。
Subject: =?UTF-8?B?44GK5ZWP44GE5ZCI44KP44Gb?=
=?UTF-8?B?44GC44KK44GM44Go44GG44GU?=
=?UTF-8?B?44GE44G+44GZ?=
隣接するencoded-word間の空白とCRLFは、デコード時に無視される仕様です。
ISO-2022-JPとの使い分け
日本語メールの文字コードには、UTF-8のほかにISO-2022-JPが長く使われてきました。古いメールシステムとの互換性が必要な場合はISO-2022-JPを使うこともあります。
=?ISO-2022-JP?B?GyRCJDMkcyRLJEEkTxsoQg==?=
現在はUTF-8が主流になりつつありますが、送信先のメールクライアントによっては文字化けする可能性があるため、利用環境に応じた選択が求められます。
nodemailerを使う場合
実務でNode.jsからメールを送信する場合は、nodemailerのようなライブラリを使うのが一般的です。nodemailerは件名の日本語をRFC 2047に自動で変換してくれるため、通常は手動でエンコードする必要はありません。
const transporter = nodemailer.createTransport({ /* ... */ });
await transporter.sendMail({
to: 'example@example.com',
subject: 'お問い合わせありがとうございます', // 自動でRFC 2047に変換される
text: '本文',
});
ただし、自前でSMTP通信を実装する場合やデバッグ時には、RFC 2047の仕組みを理解しておくことが重要です。文字化けの原因調査でメールヘッダーの生データを読む場面では、この知識が直接役立ちます。
スレッド情報
https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.4
ReferencesヘッダーとIn-Reply-Toヘッダーは、RFC 2822 標準に準拠して設定する必要があります。
https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages?hl=ja
クライアント スレッド管理方法
Gmail threadId(独自)
Outlook conversationId(独自)
Thunderbird等の従来型クライアント In-Reply-To / Referencesヘッダー(RFC 2822準拠)
In-Reply-To / References が実際に必要になるのは、RFC 2822のヘッダーだけでスレッドを判断する 従来型のメールクライアント の場合です。
Gmail・Outlookのような主要サービスはそれぞれ独自のスレッド管理を持っているので、ヘッダーがなくても動くケースが多いです。ただRFC 2822の標準に準拠しておくという意味で付けている、という方が正確ですね。