做前端或后端开发时,你一定遇到过这种情况:在 URL 里拼了一段中文参数,结果跳转后变成了一堆 %E4%BD%A0%E5%A5%BD 的天书。或者反过来,从地址栏复制一段 URL 发给别人,对方打开却乱码了。又或者 API 联调时,后端收到的中文参数全是问号。
这些问题的根源都是 URL 编码(Percent-encoding / 百分号编码)。搞懂原理,乱码问题就能系统性地解决了。
为什么 URL 里不能直接写中文
URL 标准(RFC 3986)规定,URL 里只能包含 ASCII 字符的一个子集:英文字母、数字、以及少量特殊符号(- _ . ~ : / ? # [ ] @ ! $ & ' ( ) * + , ; =)。其他所有字符——中文、日文、空格、emoji——都必须经过编码才能出现在 URL 中。
编码规则:把字符转成 UTF-8 字节序列,每个字节用 %XX(两位十六进制数)表示。
encodeURI vs encodeURIComponent:最常搞混的两个函数
JavaScript 提供了两个 URL 编码函数,名字差不多但行为完全不同。用错了就是 bug 的来源:
| 函数 | 编码范围 | 保留字符 | 适用场景 |
|---|---|---|---|
| encodeURI | 中文等非 ASCII | : / ? & = # @ + , | 编码整个 URL |
| encodeURIComponent | 所有特殊字符 | - _ . ~ ! * ' ( ) | 编码参数值 |
五个常见乱码场景及解决方案
原因:前端拼接 URL 时参数值包含 & 或 =,但只用了 encodeURI,导致参数被截断。
解决:参数值一律用 encodeURIComponent 编码。
原因:浏览器地址栏会"美化"显示中文,但实际复制出来的可能是部分编码或未编码的混合体。
解决:复制 URL 前右键 → 复制链接地址(而不是从地址栏手动选中复制),或者用纳米工房的 URL 编解码工具处理后再发送。
解决:用 decodeURIComponent 解码。如果报 "URI malformed" 错误,说明编码不完整(比如 %E4%BD 缺了最后一个字节)或者不是 UTF-8 编码。
原因:已经编码过的字符串被再次编码——% 本身被编成 %25,于是 %E4 变成 %25E4。
解决:检查代码中是否有两处地方都做了编码。只编码一次。
原因:在 application/x-www-form-urlencoded 格式中,空格编码为 +。如果参数值本身包含 +(比如 "C++"),就会被解码成空格。
解决:用 encodeURIComponent 编码 + 会变成 %2B,不会和空格冲突。
不同语言的编码函数对照
| 语言 | 编码函数 | 解码函数 |
|---|---|---|
| JavaScript | encodeURIComponent() | decodeURIComponent() |
| Python | urllib.parse.quote() | urllib.parse.unquote() |
| PHP | urlencode() / rawurlencode() | urldecode() |
| Java | URLEncoder.encode(s, "UTF-8") | URLDecoder.decode() |
| Go | url.QueryEscape() | url.QueryUnescape() |
在线编解码工具
工具会自动检测输入内容是否已编码——检测到 %XX 格式会自动解码显示原文。也支持 URL 解析模式,把完整 URL 拆解成协议、域名、路径、查询参数各个部分,方便排查编码问题。
常见问题
emoji 怎么编码?
emoji 和中文一样走 UTF-8 编码,但 emoji 通常是 4 字节字符,所以编码后更长。比如 😀 编码为 %F0%9F%98%80。
URL 有长度限制吗?
HTTP 标准没有规定上限,但实际限制来自浏览器和服务器:Chrome 约 2MB,IE 约 2083 字符,Nginx 默认 8KB。如果 URL 编码后超长,考虑改用 POST 请求把数据放 body 里。
为什么有时候看到 %u 开头的编码?
%uXXXX 是非标准的 Unicode 编码格式(JavaScript 的 escape() 函数输出),不是合法的 URL 编码。现代开发中不要使用 escape(),它已经被废弃了。