RFC 2047とは?メールの件名に日本語が使える仕組みをNode.jsの実装とあわせて解説

目次

RFCとは

RFC(Request for Comments)は、インターネット技術の仕様をまとめた文書です。

HTTP、TCP/IP、DNS、TLS、WebSocket、OAuth関連仕様、IPv6、ルーティング、時刻同期、メールなど、

RFC 5322で規定されている、Internet Message Format(IMF) が、インターネット電子メールのメッセージ標準となってます。

メール関連で特に重要なRFCは以下の通りです。

RFC内容
RFC 822
sec-3.1
メールフォーマット
https://datatracker.ietf.org/doc/html/rfc822#section-3.1
RFC 5322IMFメールフォーマット
https://datatracker.ietf.org/doc/html/rfc5322
RFC 5322
sec-3.6
ヘッダー
https://datatracker.ietf.org/doc/html/rfc5322#section-3.6
(IANAレジストリ)MIMEを含む広いヘッダー一覧を見たい場合は、IANAレジストリ
https://www.iana.org/assignments/message-headers/message-headers.xhtml
RFC 2045(MIME)パート1:インターネットメッセージ本文のフォーマット
https://datatracker.ietf.org/doc/html/rfc2045
RFC 2046(MIME)パート2:メディアの種類
https://datatracker.ietf.org/doc/html/rfc2046
RFC 2047MIME(多目的インターネットメール拡張機能)パート3:非ASCIIテキスト用のメッセージヘッダー拡張機能
https://datatracker.ietf.org/doc/html/rfc2047
RFC 2048多目的インターネットメール拡張機能(MIME)第4部:登録手続き
https://datatracker.ietf.org/doc/html/rfc2048
RFC 2049多目的インターネットメール拡張機能(MIME)第5部:適合基準と事例
https://datatracker.ietf.org/doc/html/rfc2049
RFC 2616HTTP/1.1(現在は廃止)

本記事で解説するRFC 2047は、メールヘッダー内で日本語などの非ASCII文字を扱うための仕様です。

日本語の件名はそのままメールヘッダーに記載できません — RFC 2047

ヘッダーにはASCII文字しか書けないというルール

2.2ヘッダーフィールド
ヘッダーフィールドは、フィールド名で始まり、その後にコロン(”:”)、フィールド本体が続き、CRLFで終了します。フィールド名は印刷可能な US-ASCII 文字 (つまり、33から126までの値を持つ文字(ただし、コロン。フィールド本体は、印刷可能なUS-ASCII文字で構成できます。スペース(SP、ASCII 値 32)と水平タブ(HTAB、ASCII値9)文字(まとめて空白文字と呼ばれる)文字、WSP)。フィールド本体には、CR と LF を含めてはならない。
https://datatracker.ietf.org/doc/html/rfc5322#section-2.2

つまり、日本語の件名はそのままメールヘッダーに記載できませんでした。

そもそもASCII(アスキー)とは

ASCIIは文字コードの一種です。UTF-8のような

ASCIIは、英字・数字・記号・制御文字の128種類を定義

日本語はASCIIでは表せません。

文字コード
ASCII英字・数字・記号・制御文字のみ
日本語はNG
UTF-8UTF-8ではASCIIの範囲をそのまま互換的に含む、
かつ日本語や絵文字なども扱えます

日本語など非ASCIIの制約の対策が RFC 2047

日本語のようなマルチバイト文字をそのまま入れると、中継するメールサーバーが正しく処理できず、文字化けや配送エラーの原因になります。

この問題を解決するために登場したのがRFC 2047です。

RFC 2047 — 非ASCII文字 を ヘッダーに含める方法

RFC 2047では、以下のフォーマットを使って非ASCII文字を表現します。

=?charset?encoding?encoded-text?=

各要素の意味は以下の通りです。

項目説明
charset文字コード(UTF-8、ISO-2022-JP)
encodingエンコード方式(BまたはQ)
encoded-textencofingで指定したエンコード方式で、非ASCIIからASCII文字に変換後の文字列
=?utf-8?B?${Buffer.from('日本語の件名とかの非アスキーの文字列').toString('base64')}?=

=?UTF-8?B?44GT44KT44Gr44Gh44Gv?= UTF-8 B 44GT44KT44Gr44Gh44Gv 文字コード エンコード方式 変換済みデータ 受信側はこの3つの情報をもとにデコードする

受信側はこの情報をもとに正しくデコードし、元の日本語テキストを復元できます。

エンコード方式:BとQ

RFC 2047のencodingパートには、以下の2種類が利用されます。

記号意味特徴
BBase64任意のバイト列を効率的にASCII化できる。日本語で主流
QQuoted-PrintableASCII文字が多い場合に効率的。欧文で主流

日本語のメールではBase64(B)が使われるケースが圧倒的に多いです。日本語はASCII外の文字がほとんどを占めるため、Quoted-Printableだと逆に冗長になってしまうためです。

Base64エンコードについて

エンコードとは、ある形式のデータを別の形式に変換することです。逆の操作がデコードです。

そしてBase64とは何か

Base64は、画像・ファイル・日本語を含むテキストなどのデータを、英数字や記号だけで構成された文字列に変換するエンコード方式です。

Base64とBase64URL — Node.js・ブラウザでのエンコード/デコード早わかり
https://qiita.com/idw-coder/items/7a0dc8ddb8910169a83e

JavaScriptでBase64エンコードする場合

Node.jsではBufferを使うことで、RFC 2047形式の文字列を簡潔に生成できます。

const subject = 'お問い合わせありがとうございます';

const encodedSubject =
  `=?UTF-8?B?${Buffer.from(subject).toString('base64')}?=`;

BufferはNode.jsが提供するバイナリデータのコンテナです。文字列をバイト列として扱うための中間表現だと考えてください。

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>

BuffertoString('base64')を呼ぶと、バイト列がBase64文字列に変換されます。

Buffer.from('こんにちは').toString('base64');
// '44GT44KT44Gr44Gh44Gv'

この結果はASCII文字のみで構成されているため、メールヘッダーに安全に埋め込めます。

エンコード済み文字列の長さ制限について

「エンコードされた単語」は、75文字を超えることはできません。’charset’、’encoding’、’encoded-text’、および区切り文字。エンコードされた単語に収まるよりも多くのテキストをエンコードすることが望ましい
75文字、複数の「エンコードされた単語」(CRLFスペースで区切られる)使用される。
https://datatracker.ietf.org/doc/html/rfc2047#section-2

75文字制限は元のテキストの文字数ではなく、エンコード後の =?utf-8?B?...?= という文字列全体の長さに対する制限。

例えば「あいうえお」をエンコードすると

  • UTF-8 で 15バイト(3バイト x 5文字)
  • Base64 で 20文字
  • 全体: =?utf-8?B?44GC44GE44GG44GI44GK?= → 35文字

これが75文字を超える場合、RFC 2047 的には複数の encoded-word に分割して CRLF + スペースで繋げるべき、というルール。こういう形になる

Subject: =?utf-8?B?44GC44GE44GG44GI44GK44GL44GN?=
 =?utf-8?B?44GP44GR44GT44GV44GX44GZ?=

ただし動作確認してみると、分割していない場合でも問題なく表示されてるよう、寛容なのか?

メールヘッダーは何のヘッダーか?メールの標準フォーマットIMF、機能拡張MIMEについて補足

全体の「ヘッダー + 空行 + ボディ」というテキスト形式がRFC 822で定められたメールの標準フォーマット

IMFとMIMEは完全な包含関係ではなく、MIMEがIMFの基本形式を拡張して利用する関係です。
IMFとMIMEの関係 IMFはメールの基本形式、MIMEは文字コードや添付ファイルなどを扱う拡張仕様として示す図 IMF と MIME の関係 IMF メールの基本形式 From / To / Subject Date / Message-ID In-Reply-To / References MIME メール機能の拡張 HTMLメール 添付ファイル・画像 文字コード・エンコード IMFのメッセージ構造を MIMEが拡張して利用 Content-Type / Content-Transfer-Encoding は MIME 系ヘッダー
Message-ID、In-Reply-To、References はIMF側のヘッダーです。一方、Content-Type や Content-Transfer-Encoding はMIMEで追加されたヘッダーです。

A message consists of header fields and, optionally, a body.
The body is simply a sequence of lines containing ASCII charac-
ters. It is separated from the headers by a null line (i.e., a
line with nothing preceding the CRLF).
メッセージは、ヘッダーフィールドと、場合によっては本文で構成されます。
本文は、単にASCII文字を含む行の列です。
本文は、ヌル行(すなわち、CRLFの前に何も含まれていない行)によってヘッダーから区切られています。
ヌル行とは、CRLFの前に何も含まれていない行のことです。
https://datatracker.ietf.org/doc/html/rfc822#section-3.1

MIMEとは

「Multipurpose Internet Mail Extensions」の略です。

Extensionsとある通り元々ASCII文字しか対応していなかったが、それ以外の文字やテキスト以外のデータも取り込めるように拡張した電子メールの仕様のよう

スレッド情報

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の標準に準拠しておくという意味で付けている、という方が正確ですね。

参考URL

目次