编码

URL 编码(百分号编码)指南:为什么文字会变成 %20 和 %E4%BD%A0

复制一个包含中文搜索词的链接,就会得到 %E4%BD%A0%E5%A5%BD 这样的字符串,这就是 URL 编码。本文讲解其背后的规则,以及它在网页开发、营销链接和 API 对接中引发的问题。

URL 中只允许部分字符

URL 语法由 RFC 3986 定义。只有字母、数字以及四个符号——连字符(-)、句点(.)、下划线(_)和波浪号(~)——可以原样出现在 URL 中,它们被称为非保留字符。

而 :、/、?、#、& 和 = 等符号是保留字符,用来构成 URL 的结构。? 表示查询字符串的开始,& 分隔参数,# 标记片段。如果搜索词本身包含 & 或 #,不经编码直接插入就会改变整个 URL 的含义。

百分号编码的工作原理

不能直接出现的字符会被拆成字节,每个字节写成 % 加两位十六进制数。空格(字节 0x20)变成 %20,& 符号(0x26)变成 %26,等号(0x3D)变成 %3D。

非 ASCII 文本会先转换为 UTF-8 字节,再以同样方式编码。韩文音节“안”在 UTF-8 中是 3 个字节(EC 95 88),所以变成 %EC%95%88,一个字符变成了九个字符。中文“你”同样会变成 %E4%BD%A0。

안녕 하세요?  →  %EC%95%88%EB%85%95%20%ED%95%98%EC%84%B8%EC%9A%94%3F
a b&c=d      →  a%20b%26c%3Dd

解码则是反向过程:%EC%95%88 还原为字节 EC 95 88,再按 UTF-8 解释。如果服务器改用旧的编码(如 GBK)来解释这些字节,文字就会变成乱码——这是老网站搜索词显示错误的常见原因。

空格是 %20 还是 +?

空格最容易引起混淆。按照 RFC 3986,空格是 %20。但 HTML 表单使用的 application/x-www-form-urlencoded 格式会把查询字符串中的空格写成 +。因此同样的文本 a b,在路径中会显示为 a%20b,在表单查询中则显示为 a+b。

麻烦出现在需要真正的加号时。像 +86 这样的电话号码或 1+1 这样的表达式,如果不编码直接放进查询,可能被读成空格。这类值应编码为 %2B。不同库的行为不同——JavaScript 的 encodeURIComponent 输出 %20,而一些服务器端库输出 +——所以要确认双方使用同一规则。

编码组成部分,而不是整个 URL

切勿一次性编码整个 URL。对 https://example.com/search?q=... 整体编码会把 : 和 / 变成 %3A 和 %2F,结果就不再是可用的地址。只对属于数据的部分(例如参数值)进行编码。

JavaScript 用两个函数体现了这一点。encodeURI 会保留结构性字符,而 encodeURIComponent 还会编码 /、?、& 和 =。对查询参数的值,使用 encodeURIComponent 才是安全的选择。

双重编码:看到 %2520 就要警惕

对已经编码过的字符串再次编码,% 会变成 %25。如果空格显示为 %2520 而不是 %20,说明被编码了两次。这种情况常见于经过多次重定向的追踪链接,或把另一个 URL 作为参数携带的登录回跳地址。

重复解码同样有害:数据中真正的 %25 会变成 %,值就被改变了。规则很简单——在值放入 URL 的最后一刻编码一次,在读取的第一时间解码一次。

实用检查清单

在 AIBIZAP 的 URL 编码/解码工具中试一试,就能清楚看到哪些字符会变化以及如何变化。

  • UTM 链接中的活动名称含有空格或中文时要进行编码。
  • 搜索词、电子邮件地址(含 @)、时间戳(含 :)等 API 参数值要按组成部分编码。
  • 链接失效时,查找 %25 以判断是否发生了双重编码。
  • 服务器日志出现乱码时,确认值是否按 UTF-8 解码和解释。
  • 需要把 Base64 值放进 URL 时,使用 Base64URL 或对结果进行百分号编码。

用这些工具试一试

更多指南

全部指南