中文 URL 编码后变成 %E4%B8%AD 这种 %XX,正常吗?
现象:中文变成 %XX 一串
你把 https://example.com/搜索?q=中文 复制到某处,中文变成了 https://example.com/%E6%90%9C%E7%B4%A2?q=%E4%B8%AD%E6%96%87。先放心:这是完全正常的,不是乱码。
为什么中文必须编码成 %XX
URL 的规范(RFC 3986)只允许一小部分 ASCII 字符直接出现在地址里(字母、数字和少数符号)。中文、空格、emoji 都不在白名单。为了把任意字符塞进 URL,就发明了百分号编码(Percent-encoding / URL Encoding):
- 把字符先转成 UTF-8 字节;
- 每个字节写成
%+ 两位十六进制。
所以「中」的 UTF-8 是三个字节 E4 B8 AD,编码后就变成 %E4%B8%AD。
| 原始 | UTF-8 字节 | 编码结果 |
|---|---|---|
中 | E4 B8 AD | %E4%B8%AD |
| 空格 | 20 | %20(或 +) |
A | 41 | A(无需编码) |
👍 | F0 9F 91 8D | %F0%9F%91%8D |
每个常用汉字大约膨胀成 9 个 ASCII 字符(3 字节 × 每字节 %XX)。这就是中文路径在日志、短信、二维码里显得「又长又吓人」的原因——形态变了,语义没坏。
怎么解码回来
反向操作即可:把 %XX 按字节还原成 UTF-8。打开 ToolVault 工具匣 的 URL 编解码工具:
- 把带
%XX的字符串粘进输入框; - 点「解码」,立刻得到可读中文;
- 反过来,点「编码」可以把中文转成合法 URL 片段;
- 全程本地处理,不上传。
若要分析完整 URL 的结构(协议、路径、各查询参数),用 URL 参数解析器 或 URL 解析器 更直接——按组件拆开并逐个解码。
实现注记
本站编码器是 encodeURIComponent / decodeURIComponent 的薄封装,因此:
- 中文、emoji 一律按 UTF-8 百分号化;不会走已废弃的
escape()(那会产出非法的%uXXXX)。 - 空格编码为
%20,解码不把+当空格。地址栏复制出来的查询若含+,请用参数解析工具(URLSearchParams)而不是裸decodeURIComponent。 - 半截
%会抛错并显示在输出区——常见于短信截断或网关二次编码。
与「可打印二进制」的差异见 Base64;JWT 里的 Base64URL 变体见 Base64 vs Base64URL。
什么时候要注意
- 路径 vs 查询:
/搜索和?q=搜索都可以编码;不编码在某些服务器上会被拒绝。 +的歧义:application/x-www-form-urlencoded约定+是空格,RFC 3986 里+就是加号。自己编码时用%20最不容易歧义;解码时看清对方用的是哪种解析器。- 保留字符:
?&=/#在 URL 里有语法意义。典型 bug:把用户输入直接拼进?redirect=,输入里带&就把后面的参数截断——正确做法是先encodeURIComponent再拼接。 - 双重编码:编码两次后
%E4变成%25E4,解一次还带%。有残留再解,但要设上限。
前端怎么处理:encodeURI vs encodeURIComponent
| API | 编码范围 | 适用 |
|---|---|---|
encodeURI | 保留 ://?&= 等结构字符 | 编码整条 URL |
encodeURIComponent | 连 & = / 也编码 | 编码参数值(本站工具行为) |
// 对:整条 URL 用 encodeURI
encodeURI('https://a.com/搜索?q=中') // https://a.com/%E6%90%9C%E7%B4%A2?q=%E4%B8%AD
// 对:参数值用 encodeURIComponent
const q = 'a&b=c';
`https://a.com/search?q=${encodeURIComponent(q)}`
// https://a.com/search?q=a%26b%3Dc
// 错:encodeURIComponent 用于整条 URL,会把 :// 也编码掉
encodeURIComponent('https://a.com') // https%3A%2F%2Fa.com
解码用对应的 decodeURI / decodeURIComponent。注意 decodeURIComponent 遇到孤立的 %(如字面量 100% 被误处理)会抛 URIError,生产代码里要 try/catch。
地址栏看到的 vs 线路上的
现代浏览器常把中文路径显示成可读字符,但 HTTP 线路上仍是百分号形态。从地址栏复制可能得到汉字,从 DevTools / 服务端日志复制得到 %XX——「浏览器能开、脚本打不开」往往是脚本拿到了未编码形又二次编码,或反过来。排查时用编码器对齐「线路形态」。路径与查询的空格规则也不对称,详见 空格与特殊字符 与 编解码指南。
FAQ
为什么有的网站中文直接显示,有的变成 %XX?
取决于浏览器/服务器是否做了「显示层」解码。底层传输时仍然编码,只是界面上还原给你看。
编码后链接还能正常打开吗?
能。服务器收到 %E4%B8%AD 会解码回「中」。日志和监控里两种形态并存时,去重或统计前先统一解码再比较。
Base64 和 URL 编码是一回事吗?
不是。URL 编码按字节百分号化,专用于 URI;Base64 把任意数据变可打印文本(见 Base64 工具)。base64url 把 +/ 换成 -_、去掉填充,是 JWT 里用的 URL 安全变体。
接口收到的中文是 %XX 形态,存库前要手动解码吗?
取决于框架。Express/Koa/Spring 等常自动解码查询参数;路径片段和请求体是否解码因框架而异。把收到的值打印出来,还带 %XX 就在入口统一 decodeURIComponent 一次,别在多个业务点重复解。
二次编码:看到 %25E4 就该警觉
日志里若是 %25E4%25B8%25AD 而不是 %E4%B8%AD,说明对已经编码过的串又编了一次(% → %25)。常见来源:把 encodeURIComponent 的结果再丢进会二次编码的 API,或把地址栏「显示解码」后的 URL 又塞回会编码的表单。URL 编解码 的「剥层」模式适合看样本被包了几层。
表单 body 与查询串的差异
application/x-www-form-urlencoded 传统上把空格写成 +,而现代 URLSearchParams 查询串多用 %20。中文在两边都会百分号编码,但解析器对 + 的策略不一致时,常表现为 ASCII 字段坏了、CJK「看起来还行」。每一跳只保留一种解码策略,并写在路由旁边。
本文由 ToolVault 工具匣 提供。相关工具:URL 编解码、URL 参数解析、Base64 编解码、JWT 解码。访问 首页 查看更多开发者工具。