深入理解 URL 编码:为什么空格变成了 %20
URL 编码也叫百分号编码,它让 URL 可以安全携带空格、中文、Emoji、符号和保留字符,同时不破坏 URL 本身的结构。
如果你复制链接时见过 %20、%2F、%3F、%E4%B8%AD 或 %F0%9F%98%80,那就是 URL 编码。它看起来不直观,但解决的是一个很实际的问题:URL 不只是普通文本,?、&、=、/、# 这些字符在 URL 中都有结构含义。
URL 编码为什么存在
一个 URL 通常会被拆成协议、域名、路径、查询字符串和片段:
https://example.com/search?q=hello%20world&page=1#results
在这个 URL 里:
https是协议。example.com是域名。/search是路径。q=hello%20world&page=1是查询字符串。results是片段。
如果查询参数里包含空格、&、斜杠、中文或 Emoji,浏览器和服务器就需要一种稳定方式表示它。百分号编码会把字符的 UTF-8 字节转换成 % 加两个十六进制数字。
常见字符编码表
| 字符 | 编码 |
|---|---|
| 空格 | %20 |
/ | %2F |
? | %3F |
& | %26 |
= | %3D |
# | %23 |
中 | %E4%B8%AD |
空格通常会显示为 %20。但在 application/x-www-form-urlencoded 表单数据里,空格也可能显示为 +。当开发者在表单、URL 和 API 客户端之间复制参数时,这个差异很容易导致解码结果不一致。
encodeURI 和 encodeURIComponent 的区别
JavaScript 里最常见的两个编码函数是 encodeURI() 和 encodeURIComponent()。它们名字很像,但使用场景不同。
当你已经有一个完整 URL,只想编码其中不安全的字符时,可以使用 encodeURI():
const url = 'https://example.com/search?q=hello world'
encodeURI(url)
// https://example.com/search?q=hello%20world
当你要编码一个 URL 组件,尤其是查询参数值时,应该使用 encodeURIComponent():
const value = 'hello world & JSON'
encodeURIComponent(value)
// hello%20world%20%26%20JSON
区别在于,encodeURI() 会保留 ?、&、/、= 等 URL 保留字符。对于完整 URL 来说这很合理,但如果它们出现在查询参数值里,就可能破坏参数结构。
查询参数示例
假设你想构造这个搜索地址:
https://example.com/search?q=hello world & JSON&page=1
如果 q 的值没有编码,搜索词里的 & 可能会被当成新参数的开始。更安全的写法是:
const q = 'hello world & JSON'
const url = `https://example.com/search?q=${encodeURIComponent(q)}&page=1`
结果是:
https://example.com/search?q=hello%20world%20%26%20JSON&page=1
这样服务器会收到一个完整的 q 参数,而不是错误地把搜索词拆成多个参数。
嵌套 URL 和跳转参数
另一个常见场景是把完整 URL 放进某个查询参数里,例如登录后跳转地址:
const next = 'https://example.com/dashboard?tab=billing&plan=pro'
const loginUrl = `/login?next=${encodeURIComponent(next)}`
这里的嵌套 URL 必须按组件编码。如果不编码,&plan=pro 可能会被当成 /login 的参数,而不是 next 值的一部分。
常见 URL 编码问题
当链接或 API 调用行为异常时,可以重点检查这些问题:
- 查询参数值里包含
&或=,但没有编码。 - 把完整 URL 传给了
encodeURIComponent(),导致整个链接变成不可读的编码文本。 - 参数被重复编码,导致
%20变成%2520。 - 表单把空格表示为
+,但接收方只按%20处理。 - Base64 字符串里包含
+、/或=,直接放进 URL 后被误解析。 - JSON 字符串没有先编码,就被放进查询参数。
重复编码尤其容易被忽略。如果你看到 %252F,解码一次可能得到 %2F,再解码一次才是 /。
亲自测试
你可以使用下面的工具测试编码和解码结果:
如果要整理结构化查询字符串,可以使用 URL 参数工具。如果查询参数里包含 JSON,解码后再用 JSON 格式化工具 校验。如果参数值是 Base64,可以先用 Base64 工具 检查,再放进 URL 里传输。