URL百分号编码到底是怎么工作的

百分号编码要解决的是一个很具体的问题:URL只能由有限、可预测的一组字符构成,但人们想放进URL里的内容几乎从来都不满足这个条件。

URL中能安全使用的字符范围是有限的

空格、带重音符号的字母、非拉丁文字等,都不在URL可以安全使用的字符范围内;而某些标点符号在URL的语法结构中本身就有特殊含义,所以这两类字符都需要经过转换才能出现在URL里。

保留字符具有语法含义,作为数据使用时必须编码

&、=、?、#、/这类字符定义了URL的结构(比如分隔查询参数、标记片段等)。如果这些字符需要作为字面数据出现在某个值里,而不是作为结构符号,就必须进行百分号编码,以免被误认成URL语法的一部分。

非保留字符不需要编码

字母、数字,以及少数几个符号(-、.、_、~),不带有任何特殊的语法含义,始终属于可以安全使用的字符范围,因此可以直接出现在URL中,不需要百分号编码。

%XX格式表示用十六进制表示的一个编码字节

对一个字符做百分号编码,就是先把它转换成基于UTF-8的字节表示,再把每个字节写成百分号加两位十六进制数字。空格会变成%20,是因为在ASCII/UTF-8中,空格对应的字节值正是0x20。

非拉丁字符编码后往往会变成多组%XX

一个在基本ASCII范围之外的字符,比如韩文、日文、中文,或者带重音符号的拉丁字母,在UTF-8中通常需要2到4个字节表示,所以编码后会变成2到4组%XX。这也是为什么包含非英文文字的编码后URL看起来会异常地长。

空格根据不同场景,可能被表示为%20或+

%20是表示空格的通用百分号编码,在URL的任何位置都适用。而用+号代替空格,是application/x-www-form-urlencoded这种表单提交专用的另一套较早期的惯例,并不是适用于所有URL的通用规则。

百分号编码实际解决的是什么问题

URL本来的定位,是能够在浏览器、服务器、邮件客户端等无数不同系统之间稳定传递的一段紧凑文本,而这些系统在历史上很多只能安全处理有限的ASCII字符集。百分号编码把超出这个安全范围的字符,以及被当作数据使用的保留字符,统一转换成基于该字符UTF-8字节表示的标准%XX格式,从而保证不管是哪种系统在处理这段字符串,都不会产生歧义,也能安全传递。

真正会导致编码URL出问题的错误

重复编码——对一个已经做过百分号编码的字符串再编码一次——是实际开发中很常见的一类问题,它会把已经存在的%符号变成%25,以一种从外观上不容易察觉的方式把URL悄悄破坏掉。另一个常见的混淆,是把+和%20当成任何场合都可以互换的东西:+表示"空格"只在表单编码的查询数据里成立,如果在URL路径等其他位置使用+,很可能会被当成字面上的加号,而不是空格。

常见问题

为什么一个中文、日文或韩文字符编码后,会变成一长串百分号?

因为这些字符不在基本的ASCII范围内,在UTF-8中通常需要用多个字节表示(很多中日韩文字常见的是3个字节),而每一个字节都会被单独编码成一组%XX,所以一小段非拉丁文字,编码后的字符串长度会明显变长。

表示空格的%20和+,实际区别是什么?

%20是通用的空格百分号编码,在URL的任何地方都有效。而用+表示空格,是仅适用于application/x-www-form-urlencoded表单数据的一种更早期、更局限的惯例,在这个场景之外使用+,很可能会被解读成字面上的加号,而不是空格。