cv.cm

知识库 · 文本、数据与安全

什么是 URL 编码?

阅读约 5 分钟

简短回答

URL 编码,正式名称为百分号编码(percent-encoding),会把 URL 中不允许出现或有特殊含义的字符,按每个字节替换成 % 加两位十六进制数。空格变成 %20,& 变成 %26;非 ASCII 文本先转成 UTF-8,所以「é」变成 %C3%A9。字符串「a b&c」编码后是 a%20b%26c。

打开URL 编码

为什么 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。在标准服务器上,两种形式解码后的值相同。

常见错误

  1. 重复编码:对已经编码过的文本再次编码,会把 %20 变成 %2520,因为 % 本身被编码成了 %25。每个值只在放入 URL 的那一刻编码一次。
  2. 用组件函数编码整个 URL:https:// 会变成 https%3A%2F%2F,链接就失效了。
  3. 路由前先解码:服务器如果在匹配路由前就把 %2F 解码成 /,就可能被诱导去读取另一个路径。
  4. 沿用旧字符集:一些老系统把「é」编码成 %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。

全部知识文章