知识库 · 文本、数据与安全
什么是 URL 编码?
简短回答
URL 编码,正式名称为百分号编码(percent-encoding),会把 URL 中不允许出现或有特殊含义的字符,按每个字节替换成 % 加两位十六进制数。空格变成 %20,& 变成 %26;非 ASCII 文本先转成 UTF-8,所以「é」变成 %C3%A9。字符串「a b&c」编码后是 a%20b%26c。
为什么 URL 需要编码
RFC 3986 把 URL 限制在一小组 ASCII 字符内,其中一些还身兼要职:? 开始查询串,& 分隔参数,= 分开参数名和值,# 开始片段,/ 分隔路径段。如果某个值里包含这些字符,URL 就会产生歧义。
以搜索「salt & pepper」为例。直接写成 ?q=salt & pepper,服务器看到的是参数 q 的值为 salt ,以及另一个名为 pepper 的空参数。编码成 ?q=salt%20%26%20pepper 后,它才会作为一个完整的值到达服务器。
哪些字符需要编码
| 类别 | 字符 | 规则 |
|---|---|---|
| 非保留字符 | A–Z a–z 0–9 - . _ ~ | 永远不需要编码 |
| 保留字符(gen-delims) | : / ? # [ ] @ | 出现在值里时需要编码 |
| 保留字符(sub-delims) | ! $ & ' ( ) * + , ; = | 出现在值里时需要编码 |
| 其他所有字符 | 空格、"、<、>、%、非 ASCII 字符 | 一律编码 |
编码针对的是字节而不是字符。非 ASCII 文本先转成 UTF-8,每个字节各自变成一个 %XX 三元组:
| 输入 | UTF-8 字节 | 编码结果 |
|---|---|---|
| 空格 | 20 | %20 |
| & | 26 | %26 |
| % | 25 | %25 |
| é | C3 A9 | %C3%A9 |
| 日本 | E6 97 A5 E6 9C AC | %E6%97%A5%E6%9C%AC |
十六进制数字不区分大小写,%c3%a9 和 %C3%A9 含义相同,但 RFC 3986 建议统一使用大写。
%20 还是 +:空格的两套规则
RFC 3986 定义的百分号编码总是把空格写成 %20。HTML 表单提交使用的是一种相关格式 application/x-www-form-urlencoded,其中空格变成 +,而真正的加号变成 %2B。
- 在
/files/my report.pdf这样的路径中,用%20。路径中的+就是字面意义上的加号。 - 在查询串中,按表单编码解析的服务器会把
+读成空格。%20两种约定都认,拿不准时用它更稳妥。 - 如果值里可能出现真正的
+,比如+84这样的电话号码,必须以%2B发送,否则到达时会变成空格。
JavaScript 中 encodeURIComponent 与 encodeURI 的区别
| 函数 | 不编码的字符 | 适用于 |
|---|---|---|
encodeURIComponent() | A–Z a–z 0–9 - _ . ! ~ * ' ( ) | 单个值:一个查询参数或一个路径段 |
encodeURI() | 上述字符,外加 ; , / ? : @ & = + $ # | 已经组织好结构的完整 URL |
URLSearchParams | 表单编码规则(空格变成 +) | 构建或读取查询串 |
encodeURIComponent("a b&c") 返回 a%20b%26c,而 encodeURI("a b&c") 返回 a%20b&c,因为它默认 & 是分隔符。常见的 bug 就是对参数值调用 encodeURI,导致 &、= 和 # 原样通过,把值拆散了。
new URLSearchParams({ q: "a b&c" }).toString() 得到 q=a+b%26c。在标准服务器上,两种形式解码后的值相同。
常见错误
- 重复编码:对已经编码过的文本再次编码,会把
%20变成%2520,因为%本身被编码成了%25。每个值只在放入 URL 的那一刻编码一次。 - 用组件函数编码整个 URL:
https://会变成https%3A%2F%2F,链接就失效了。 - 路由前先解码:服务器如果在匹配路由前就把
%2F解码成/,就可能被诱导去读取另一个路径。 - 沿用旧字符集:一些老系统把「é」编码成
%E9(Latin-1)。现代浏览器和 WHATWG URL 标准都使用 UTF-8。
常见问题
为什么 URL 中的空格是 %20?
URL 中不允许出现空格,所以要替换成 % 加该字节的十六进制值。空格的 ASCII 码是十进制 32,即十六进制的 20。
URL 中的 %2F 是什么意思?
%2F 是编码后的正斜杠。它让 / 可以出现在值里,而不被当作路径分隔符。
URL 编码和 Base64 是一回事吗?
不是。URL 编码只替换不安全的字符,其余部分保持可读。Base64 则把所有字节转换成 64 个字符的字母表,而且它的标准字符 + 和 / 本身也需要 URL 编码,除非使用 URL 安全变体。
带重音的字母或中文需要编码吗?
需要,在实际请求中必须编码。浏览器地址栏会以可读形式显示它们,但发送的是 UTF-8 百分号编码形式,比如「日」是 %E6%97%A5。