深入理解 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 里传输。