跳到主要内容
编码转换2026-08-284 分钟阅读

中文 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(或 +)
A41A(无需编码)
👍F0 9F 91 8D%F0%9F%91%8D

每个常用汉字大约膨胀成 9 个 ASCII 字符(3 字节 × 每字节 %XX)。这就是中文路径在日志、短信、二维码里显得「又长又吓人」的原因——形态变了,语义没坏。

怎么解码回来

反向操作即可:把 %XX 按字节还原成 UTF-8。打开 ToolVault 工具匣 的 URL 编解码工具:

  1. 把带 %XX 的字符串粘进输入框;
  2. 点「解码」,立刻得到可读中文;
  3. 反过来,点「编码」可以把中文转成合法 URL 片段;
  4. 全程本地处理,不上传。

若要分析完整 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 解码。访问 首页 查看更多开发者工具。