摘要

  • 1982 年 8 月的 RFC 822把正式发表的邮件扩展字段与用户自定义字段分开。正式扩展不得以 X- 开头,私人字段因此拥有一块受保护的名字空间。
  • 保护区后来成了迁移账单。1997 年 1 月的 RFC 2068要求 HTTP 为兼容旧实现,把 x-gzip、x-compress 分别视作 gzip、compress。2007 年 12 月的 RFC 5064确立 Archived-At 时,仍须描述并登记其旧名 X-Archived-At。
  • 2012 年 6 月的 RFC 6648废止了对新文本参数使用 X- 的惯例。接收程序不能凭前缀推断字段是否标准或安全;协议设计者应通过易用的登记程序记录状态。

给民间试验划一排柜子

一封邮件的头字段由名字和值组成。程序先看名字,才知道怎样处理值。两个团队若碰巧为不同东西用了同一个名字,消息一旦跨越它们的系统就会出错。给私人试验留出一片名字空间,本是很实际的办法:不必等委员会批准便能试新功能,同时也不占正式标准未来可能用的名字。

1982 年 8 月发布的 RFC 822正是这样安排邮件头的。它区分正式发表的扩展字段和用户自己定义的字段,并规定前者永远不能以 X- 开头。后者得到一个受保护的命名区域。地方团队可以往消息里写附加信息,而不用先为一个字段走完整的标准化流程。

问题藏在“名字”两个字里。人看到 X-,会理解为“现在是私人试验”;程序看到它,只会把它当作字段标识的一部分。一个会读 X-Example 的旧程序,不会因为标准化机构后来批准了 Example 就自动学会新拼法。制度上的身份可以一天内改掉,线上软件和历史档案里的字节却不会在同一天跟着改。

试验尚无人使用时,换名成本很低。一旦发件程序、收件程序和归档系统都认这个名字,正式命名反而可能增加一个新名字,而旧名字继续占着柜子。前缀说的是“临时”,兼容性说的是“先别动”。

HTTP 提前交了双份名字的钱

这个代价在 1997 年 1 月的 RFC 2068里写得很具体。HTTP 内容编码部分要求,为了兼容此前的实现,应用程序应把 x-gzip 当作 gzip,把 x-compress 当作 compress。标准作者没有机会只选一个干净的新名字。旧名字已经进入实现,规范只能承认两种拼法要指向同一编码。

这不是文字洁癖。发送端可以用任一种名字表示内容编码;只会识别另一种的接收端可能无法正确处理内容。转发、缓存和归档的中间系统也必须知道两者关系。实验标记不再只是开发者的便签,它成了协议接收规则的一部分。

邮件也走过类似的路。2001 年 4 月的 RFC 2822取代 RFC 822,描述可选字段时不再沿用正式扩展与用户字段的旧语法划分。规则可以从新规范中撤掉,旧邮件和读旧邮件的程序却仍然存在。管理上的分类变化不会重写已经发送的头字段。

2007 年 12 月的 RFC 5064提供了更清楚的现场。它标准化 Archived-At,让邮件能带一个指向归档副本的 URI。同时,它为兼容旧系统描述了前身 X-Archived-At:接收程序可以解析这个字段,发送端则不宜再生成它。旧名被登记为已废弃,新名也进了登记册。一次标准化仪式,留下两个仍需识别的名字。

这两个案例各有自己的规则。HTTP 文档明确要求两组特定编码名等价;归档字段文档允许读取旧形式,同时劝发送端使用新形式。它们并没有统计当年有多少软件采用每一种名字。要回答普及程度,需要有日期的实现记录或消息样本。双名兼容这项实际工作,则已经明明白白写在原始规范中。

前缀失去“身份徽章”

2012 年 6 月作为 IETF 最佳现行实践发表的 RFC 6648总结了这个陷阱。一个非标准参数可能逐渐进入公共使用;把它升为标准时改名,旧软件只懂带前缀的形式,新软件可能只懂不带前缀的形式。为了互通,新软件往往长期保留旧拼法。所谓实验区便在没有正式手续的情况下成了事实上的标准区。

这份文件针对新定义的应用层文本参数,不是命令全网清除现有 X-。旧字段是否迁移,由维护者决定;私人或实验性扩展本身仍然可以存在;某个协议已有的专门规则也不会被自动推翻。文件撤销的是一条命名建议:不要把一个想法当前的身份烙进它在网络上传输的名字里。

它还约束接收程序的判断。不能只凭 X- 的有无推断字段是否标准,也不能把没有前缀的名字视为安全、带前缀的视为不安全。名字用于识别,不是注册凭证,更不是安全审查报告。若程序把两字符当作权限标签,实际的标准状态和风险就会被挡在视线外。

RFC 6648 给协议设计者的替代办法,是建立容量充足、登记手续简明的名字注册表,必要时分设临时与永久登记。前缀名字也可以登记;登记状态不由拼写决定。这样一个试验将来成熟时,注册表可以更新它的身份说明,接收程序未必需要学习第二个字段名。

还有一个更尖锐的边界:经过标准化和安全审查,新字段的行为可能与试验版本不同。此时无脑删掉 X- 并把两者视为同义,反而会制造新的互通或安全问题。RFC 2068 针对 x-gzip 给出了确切等价规则;它没有授予所有字段通用的“去掉前缀”操作。每一组新旧名字都要查自己的语义。

实验成功,旧名便开始收租

1982 年的安排给私人试验留出了自由。2012 年的逆转承认:一个好用的名字不会因为前缀说它“临时”就保持临时。程序运行它,档案保存它,其他系统依赖它。在标准组织宣布新名字以前,旧名字已经有了使用者。

原始文件足以看见账单。1997 年的 HTTP 规范承认双名等价;2007 年的邮件规范把弃用的旧名正式登记在新名旁边。具体某一年多少程序发过两种形式,仍需有日期的实现清单或消息记录。已能确定的是,机构掌握登记和建议,运行中的软件与存储中的消息掌握能否立即删名。

RFC 6648 没有清空旧柜子。它只是叫新协议别再造一排。试验的身份交给登记和审查去记录,名字则可以在身份改变时继续稳定。少了那两个字母,未来也就少一笔预埋的改名债。