对任意文字做URL编码或解码
对查询参数值、路径片段或整个网址做百分号编码,也可以把%XX序列还原成可读文字。支持UTF-8、表单式的+号空格,以及逐行处理。内容不会离开你的浏览器。
如何对文字做URL编码或解码
- 把文字、参数值或网址粘贴到输入框中。
- 选择编码类型。只处理单个查询参数值或路径片段时,保持组件即可;如果是一个完整地址,只需要修正其中的空格和非ASCII字符,选择完整网址;如果数据将按HTML表单提交的方式被读取,选择表单。
- 点击编码或解码。勾选每行一个值后,每一行会单独处理,因此可以一次转换整列参数或网址。
- 复制结果,或点击将结果作为输入再运行一次,例如把被编码了两次的值逐层还原。
解码带有查询字符串的完整网址时,工具还会以明文列出每个参数的名称和值,这是阅读一长串追踪链接或跳转链接最快的办法。
什么是百分号编码
网址只能包含有限的一组ASCII字符,而其中一些字符有特定用途:?开启查询字符串,&分隔参数,#开启片段,/分隔路径片段。其余内容要放进网址,就靠百分号编码:每个字符先转换成UTF-8字节,再把每个字节写成%加两位十六进制数。空格只有一个字节,所以变成%20;é有两个字节,写作%C3%A9;像😀这样的表情符号有四个字节,写作%F0%9F%98%80。汉字通常占三个字节,例如“书”会变成%E4%B9%A6。解码就是把这个过程反过来。
这些规则来自定义URI语法的标准RFC 3986第2节。它建议十六进制数字使用大写(写%3A,不写%3a),要求文字先按UTF-8转为字节,并提醒实现方不得对同一个字符串重复进行百分号编码或解码。
保留字符与非保留字符(RFC 3986)
RFC 3986把网址可以包含的字符分成两组:
- 非保留字符:字母
A–Z a–z、数字0–9以及- . _ ~。它们永远不需要编码,编码与否含义相同。 - 保留字符:通用分隔符
: / ? # [ ] @和子分隔符! $ & ' ( ) * + , ; =。它们可能承载网址的结构,所以当其中某个字符是你的数据而不是网址语法的一部分时,必须做百分号编码。
其余字符,包括空格、%本身、引号、尖括号和所有非ASCII字符,都不能原样出现在网址中,必须编码。最常见的几个如下:
| 字符 | 编码后 | 为什么重要 |
|---|---|---|
| 空格 | %20(表单数据中为+) | 网址中根本不允许出现 |
& | %26 | 会开启一个新的查询参数 |
= | %3D | 会把参数名和参数值分开 |
? | %3F | 会开启查询字符串 |
# | %23 | 会把其后的所有内容划为片段,而片段永远不会发送到服务器 |
/ | %2F | 会多出一个路径片段 |
+ | %2B | 在表单编码的查询字符串里会被读成空格 |
% | %25 | 会被当作转义序列的开头 |
: @ | %3A %40 | 分隔协议、账号信息和主机 |
é | %C3%A9 | 非ASCII字符,按两个UTF-8字节编码 |
组件、完整网址、RFC 3986还是表单:该选哪个
同一段输入,会因为结果的去向不同而编码成不同的样子。以输入Tom & Jerry's (2024)/ü为例:
- 组件得到
Tom%20%26%20Jerry's%20(2024)%2F%C3%BC。这就是JavaScript的encodeURIComponent,除字母、数字和- _ . ! ~ * ' ( )外全部编码。适用于单个查询参数值、单个路径片段,或者要放进另一个网址参数里的网址,比如跳转目标。 - 严格RFC 3986得到
Tom%20%26%20Jerry%27s%20%282024%29%2F%C3%BC。它额外编码! ' ( ) *:RFC 3986把这几个字符列为子分隔符,而encodeURIComponent却放过了它们。当严格的解析器或请求签名方案要求完全符合RFC字符集时使用。 - 表单得到
Tom+%26+Jerry%27s+%282024%29%2F%C3%BC。它遵循WHATWG URL标准中的application/x-www-form-urlencoded格式:只有字母、数字和* - . _保持原样,空格变成+。HTML表单和URLSearchParams发送的就是这种格式。 - 完整网址得到
Tom%20&%20Jerry's%20(2024)/%C3%BC。这是encodeURI,它保留; / ? : @ & = + $ , #不动,因为它认为这些字符在完整网址里已经各司其职。只适合整理含有空格或非ASCII文字的完整地址,千万不要用来处理参数值:值里的&会悄悄开启一个新参数。
%20还是+:网址中的空格
两种约定并存。在RFC 3986的网址语法里,空格永远是%20,而+只是一个普通的加号。在浏览器用GET提交表单时使用的表单编码格式里,空格写成+,该格式对应的WHATWG解析器会先把每个+替换成空格,再做百分号解码。所以?q=a+b在表单数据解析器看来是“a b”,而路径里的/a+b则表示“a+b”。
这就是解码器提供解码时将+视为空格选项的原因。对于表单、搜索框和大多数统计工具生成的查询字符串,请勾选它;处理路径,以及加号本身有意义的值时请不要勾选,比如name+tag@example.com这样的邮箱地址,编码后是name%2Btag%40example.com。
代码中的对应写法
- JavaScript:处理参数值用
encodeURIComponent()和decodeURIComponent(),处理完整网址用encodeURI(),需要表单式+空格的查询字符串用URLSearchParams(参见MDN)。 - Python:
urllib.parse.quote()默认不编码/,空格写成%20,适合路径;quote_plus()会编码/并把空格写成+,适合表单值(参见Python文档)。 - PHP:
rawurlencode()遵循RFC 3986,空格写成%20;urlencode()则把空格写成+(参见PHP手册)。
常见的URL编码问题
重复编码:%2520而不是%20
如果网址里出现%2520、%253A之类的内容,说明有程序对已经编码过的字符串又编码了一次;%25就是编码后的百分号。先解码一次,看看结果。当解码结果里仍含有百分号序列时,本工具会给出提醒,你可以点击将结果作为输入继续解码下一层。代码层面的修复办法是:在拼接网址的那一刻,对每个值只编码一次。
“Malformed URI sequence”错误
严格的解码器会拒绝后面没有跟着两位十六进制数的%,以及不合法的UTF-8字节序列。本解码器则比较宽容:它会解码所有有效的序列,保留孤立的%,把无效字节替换成�替换字符,并告诉你发生了这些情况,这样你就能找到出错的位置,而不是什么结果都得不到。
参数值在#或&处被截断
如果某个参数到达时被截断了,说明值里含有未编码的#或&,通常是因为网址是用字符串拼接,或用“完整网址”模式编码出来的。请改用“组件”模式对值编码。
对整个网址编码后网址坏了
把一个完整网址放进“组件”模式,会把https://变成https%3A%2F%2F。只有当这个网址本身是另一个网址里的参数值时,这样才是对的。否则请只对要插入的那一部分编码,或者使用“完整网址”模式。
相关的网址工具
用UTM链接生成器生成完整的活动链接,用网址清理工具去掉追踪参数,用重定向检查工具查看一个链接经过的每一次跳转。想把脚本做成一键使用的浏览器书签,书签小程序生成器会替你做好百分号编码。如果你在工作时顺手保存RFC这类参考文档,Marqly会把它们放进同一个可搜索的资料库,详见面向开发者的Marqly。
常见问题解答
URL编码是做什么的?
它会把网址中不安全或有特殊含义的字符,按UTF-8字节逐个替换成百分号加两位十六进制数。空格变成%20,&变成%26,é变成%C3%A9。这样任何文字都能放进网址里传递,而不会被误认为网址自身的结构。
encodeURI和encodeURIComponent有什么区别?
encodeURIComponent会编码除字母、数字以及 - _ . ! ~ * ' ( ) 之外的所有字符,所以适合处理单个查询参数值或路径片段。encodeURI还会保留 ; / ? : @ & = + $ , # 不动,因为它假定你传入的是已经结构完整的网址。处理参数值请用“组件”模式,只有整个地址才用“完整网址”模式。
空格应该写成%20还是+?
两种写法在不同场景下都是对的。%20在网址的任何位置都有效;加号只在表单编码的查询字符串(application/x-www-form-urlencoded)里表示空格,HTML表单和URLSearchParams生成的就是这种格式。在路径中,加号就是普通的加号。解码来自表单的查询字符串时,请勾选“解码时将+视为空格”。
为什么我的网址里出现了%2520?
因为它被编码了两次。第一次把空格变成%20,第二次又把%本身编码成%25,于是得到%2520。RFC 3986规定,同一个字符串不应被编码或解码超过一次。先解码一次,看是否还原成%20,再解码一次即可。
可以一次编码或解码很多网址吗?
可以。每行粘贴一个值,并保持勾选“每行一个值”。每一行会单独编码或解码,换行会被保留,而不会变成%0A。
哪些字符需要编码?
凡是当作“值”而不是网址结构的一部分时,RFC 3986非保留字符集(字母、数字、连字符、句点、下划线、波浪号)之外的任何字符都必须编码。其中包括空格、非ASCII字符、%,以及 ? # & = / : @ 和 + 这类保留分隔符。
我输入的文字会被上传吗?
不会。编码、解码和查询字符串拆解都在你的浏览器里用内置的JavaScript函数完成,不会向任何服务器发送数据。





