RFC 2047:MIME 第3部分 — 非ASCII文本的消息头扩展
为什么存在这个标准
RFC 2045 和 RFC 2046 解决了在邮件正文中传输非ASCII内容的问题,使用内容传输编码。但电子邮件头(主题、发件人显示名称、收件人显示名称等)受 RFC 5322 管制,仅限于7位美国ASCII字符。
这造成了显而易见的问题:数十亿电子邮件用户使用需要非ASCII字符的语言编写邮件。没有RFC 2047,你无法发送包含以下内容的电子邮件:
- 中文、日文、韩文、阿拉伯文、希伯来文或泰文主题行
- 含有重音字符的发件人显示名称(René、Müller、Björk)
- 任何包含ASCII范围外字符的头字段
RFC 2047 定义了编码字语法:一种在仅支持ASCII的头中嵌入非ASCII文本的紧凑方式,任何支持MIME的邮件客户端都可以读取。
工作原理
编码字格式
编码字具有以下结构:
=?charset?encoding?encoded-text?=
三个分量为:
| 分量 | 目的 | 值 |
|---|---|---|
charset |
原始文本的字符集 |
UTF-8、ISO-8859-1、ISO-2022-JP 等 |
encoding |
文本编码为ASCII的方式 |
B(base64)或 Q(引用可打印变体) |
encoded-text |
编码表示 | 仅ASCII字符 |
B 编码(Base64)
使用标准base64编码。最适合非ASCII文本繁重的文本,例如CJK文字:
; 主题:"会议确认"用日文表示 Subject: =?UTF-8?B?5Lya6K2w44Gu56K66KqN?= ; 发件人显示名称为中文 From: =?UTF-8?B?5byg5LiJ?= <zhang@example.com>
Q 编码(引用可打印变体)
针对头优化的修改引用可打印编码。与正文QP类似,非ASCII字节变为 =XX 十六进制对。关键差异:空格编码为下划线(_):
; 主题:"Café menu"含有重音e Subject: =?UTF-8?Q?Caf=C3=A9_menu?= ; 发件人显示名称:"René Dupont" From: =?UTF-8?Q?Ren=C3=A9_Dupont?= <rene@example.com> ; 主题:"Gruße aus Berlin"(德文问候) Subject: =?UTF-8?Q?Gru=C3=9Fe_aus_Berlin?=
当大部分文本为ASCII且仅有少数非ASCII字符时,Q编码更易读。当大部分字符为非ASCII时,B编码更紧凑。
编码字可以出现的位置
编码字允许在头的特定位置出现:
-
主题、注释、关键词:预期文本的任何地方(作为
atom或quoted-string的替代)。 - 发件人、收件人、抄送、密送、回复地址、发件人:仅在显示名称部分,从不在电子邮件地址本身。
- 内容描述:允许用于描述MIME部分。
编码字不允许在引号字符串内、电子邮件地址的本地部分或域中,或作为结构化头(如Content-Type)中的参数值(对此使用 RFC 2231)。
关键技术细节
长度限制
每个编码字不得超过75个字符。如果编码文本更长,必须分成多个编码字,用折叠空格(CRLF +空格或制表符)分隔:
; 长主题分成两个编码字 Subject: =?UTF-8?B?5LuK5pel44Gu5Lya6K2w44Gr44Gk44GE44Gm?= =?UTF-8?B?44GU5qGI5YaF44GE44Gf44GX44G+44GZ?=
当两个相邻编码字仅由线性空格分隔时,解码期间忽略它们之间的空格。这允许长文本无缝分割成多个编码字。
字符集选择
新邮件始终使用 UTF-8。其他字符集出于遗留原因而存在:
| 字符集 | 用例 | 建议 |
|---|---|---|
UTF-8 |
涵盖所有Unicode字符 | 始终使用 |
ISO-8859-1 |
西欧遗留 | 不要在新邮件中使用 |
ISO-2022-JP |
日文遗留编码 | 仍能从某些日文邮件客户端看到 |
GB2312 |
简体中文遗留 | 不要在新邮件中使用 |
与头折叠的交互
RFC 5322 将头行限制为998个字符,建议保持在78个以下。编码字与折叠交互:可以在空格边界处在编码字之间中断,但绝不能在编码字中间中断。=?...?= 包装必须在单行上。
解码规则
当邮件客户端遇到编码字时,它:
- 从
=?charset?encoding?text?=包装中提取字符集、编码类型和编码文本。 - 使用base64(B)或引用可打印(Q)解码文本。
- 根据声明的字符集解释生成的字节。
- 向用户显示解码的Unicode文本。
如果客户端不识别该字符集,应按原样显示编码字,而不是显示乱码文本。
示例
包含编码头的完整邮件
MIME-Version: 1.0 From: =?UTF-8?Q?Ren=C3=A9_Dupont?= <rene@example.fr> To: =?UTF-8?B?5bGx55Sw5aSq6YOO?= <yamada@example.jp> Subject: =?UTF-8?Q?Re:_R=C3=A9union_du_15_mars?= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Bonjour Taro, Confirmons la r=C3=A9union pour le 15 mars.
注意:头使用RFC 2047编码字(=?...?=),而正文使用常规引用可打印编码(=XX,没有包装)。这些是用于邮件不同部分的不同机制。
编码比较
同一文本——"München"——以两种方式编码:
; Q编码:易读,适合大多为ASCII文本 =?UTF-8?Q?M=C3=BCnchen?= ; B编码:紧凑但不透明 =?UTF-8?B?TcO8bmNoZW4=?=
常见错误
-
编码电子邮件地址本身。仅显示名称可以编码。
=?UTF-8?Q?user?=@example.com无效,将被拒绝或误解。有关国际化电子邮件地址,请参阅RFC 6531/6532。 -
编码字与常规文本之间缺少空格。编码字必须与相邻文本用空格分隔。
Hello=?UTF-8?Q?World?=格式不正确;应为Hello =?UTF-8?Q?World?=。 -
编码字跨行中断。整个
=?...?=令牌必须适应单行。如需折叠,在字边界处分成多个编码字。 -
在Content-Type参数中使用RFC 2047。编码字在结构化头参数(如
filename=或name=)中无效。改用 RFC 2231 参数编码:filename*=UTF-8''R%C3%A9sum%C3%A9.pdf。 - 超过75字符限制。每个编码字必须为75个字符或更少。长文本必须分成多个编码字。超大编码字可能被邮件服务器静默截断。
-
双重编码。对已编码的文本再次编码会产生垃圾,如
=?UTF-8?Q?=3D=3FUTF-8=3FQ=3F...?=。确保编码管道恰好运行一次。
可投递性影响
- 不正确的编码触发垃圾邮件过滤器。主题行中格式错误的编码字是危险信号。垃圾邮件过滤器已看到数十年来自垃圾邮件工具的损坏编码。干净、符合标准的编码标志合法的发送软件。
-
显示名称编码影响信任。如果发件人显示名称中的非ASCII字符未正确编码,收件人会看到原始
=?UTF-8?Q?...?=文本而不是可读名称。这看起来可疑且会伤害打开率。 - 主题行渲染对参与度至关重要。由于错误的字符集或损坏的编码导致主题行乱码意味着收件人无法读取它。电子邮件被忽略或报告为垃圾邮件。
- 始终使用UTF-8。ISO-8859-1等遗留字符集无法表示所有字符。如果系统在不同头间混合字符集,客户端可能正确显示某些字符而其他字符为乱码。标准化为处处使用UTF-8。
- 跨客户端测试。Outlook、Gmail、Apple Mail和Thunderbird都有略微不同的RFC 2047解码行为,特别是在长编码字和单头中混合编码/非编码等边界情况下。