URL Encoding Explained (Percent-Encoding, Simply)
URL 编码详解(简单讲清百分号编码)
If you have ever stared at a URL full of %20 and %E4%B8%AD and wondered what was going on, this guide is for you. URL encoding (also called percent-encoding) is the mechanism that lets URLs carry any character at all, from spaces to emoji to CJK ideographs. It is not magic, and once you understand the rules you can read, write, and debug URLs confidently.
如果你曾经盯着一个充满 %20 和 %E4%B8%AD 的 URL 不知所措,这篇指南就是为你准备的。URL 编码(也叫百分号编码)让 URL 能承载任意字符 —— 从空格到 emoji 到中日韩表意文字。它并不神秘,一旦你掌握了规则,就能自信地读、写、调试 URL。
The basic rule
基本规则
A URL may only contain a restricted set of characters. Anything outside that set is replaced with a % followed by the byte value in hexadecimal. So a space (ASCII 0x20) becomes %20, a literal % becomes %25, and the Chinese character 中 (UTF-8 bytes 0xE4 0xB8 0xAD) becomes %E4%B8%AD.
URL 只能使用受限的字符集。任何不在该集合内的字符都会被替换为 % 后跟该字节的十六进制值。所以空格(ASCII 0x20)变成 %20,字面 % 变成 %25,中文字符 中(UTF-8 字节 0xE4 0xB8 0xAD)变成 %E4%B8%AD。
Two flavors: encodeURI vs encodeURIComponent
两种风格:encodeURI 与 encodeURIComponent
JavaScript gives you two functions, and choosing the wrong one is the most common source of URL bugs.
JavaScript 提供两个函数,选错是 URL 错误最常见的来源。
encodeURIComponent escapes everything except A–Z a–z 0–9 - _ . ! ~ * ' ( ). Use it for query parameter values and individual path segments, where any structural character would be interpreted as syntax.
encodeURIComponent 会转义除 A–Z a–z 0–9 - _ . ! ~ * ' ( ) 之外的所有字符。用于查询参数值和单个路径段 —— 这些位置任何结构字符都会被当作语法解析。
encodeURI preserves URL syntax characters like / : ? # & = + $ , ;. Use it when you want to encode a full URL without breaking its structure.
encodeURI 保留 URL 结构字符 / : ? # & = + $ , ;。当你需要编码完整 URL 但保留其结构时使用。
Quick test:
快速测试:
encodeURIComponent("a&b/c?d=e") → "a%26b%2Fc%3Fd%3De"
encodeURI("https://example.com/a&b/c?d=e") → "https://example.com/a&b/c?d=e" (unchanged, since &, /, ?, = are preserved)
Try the URL encoder / decoder to experiment with both modes on real input.
用 URL 编码 / 解码工具 在真实输入上试验两种模式。
When encoding matters
什么时候必须编码
- Query parameter values that contain spaces, &, =, #, or non-ASCII characters. Skipping this is how "John & Jane" becomes "John & Jane=undefined" in your server logs.
- 包含空格、&、=、# 或非 ASCII 字符的查询参数值。漏掉这步,"John & Jane" 在服务端日志里就变成 "John & Jane=undefined"。
- Path segments that contain slashes, dots, or unicode. A file name like report 2026.pdf must become report%202026.pdf.
- 包含斜杠、点或 Unicode 的路径段。像 report 2026.pdf 这样的文件名必须编码为 report%202026.pdf。
- Form submissions with the default
application/x-www-form-urlencodedcontent type. The browser encodes the form data automatically — but if you build the body yourself, you must encode it. - 使用默认
application/x-www-form-urlencoded内容类型的表单提交。浏览器会自动编码表单数据,但如果你手动构造请求体,就必须自己编码。 - Anything you put in a JWT or OAuth state parameter. These get sent through a URL, so any reserved characters must be percent-encoded (or use Base64URL, which avoids the issue).
- JWT 或 OAuth state 参数中的任何内容。这些都会通过 URL 传输,任何保留字符都必须做百分号编码(或者用 Base64URL 来规避)。
Common pitfalls
常见坑
Encoding twice. A common bug: client encodes a value, server decodes it, then the server encodes it again before persisting or echoing it. The final result is doubly-encoded and looks like garbage. Always be clear about who is responsible for encoding at each boundary.
重复编码。常见 bug:客户端编码一次,服务端解码,然后服务端再编码一次再保存或回显。最终结果是双重编码,看起来像乱码。一定要在每个边界明确谁负责编码。
The plus sign. In query strings, + is conventionally interpreted as a space (it comes from form-urlencoded data). If you actually want a plus sign in a value, encode it as %2B. Otherwise it will silently turn into a space when the server decodes the query string.
加号。在查询字符串中,+ 传统上被解释为空格(来自 form-urlencoded 数据)。如果你真的想保留加号,把它编码为 %2B。否则服务端解码查询字符串时,它会悄悄变成空格。
Unicode normalization. The character "é" can be one or two code points depending on whether it was written as a single codepoint (U+00E9) or as "e" + combining acute accent (U+0065 U+0301). The percent-encoded UTF-8 forms are different, so a naive equality check can fail. Normalize to NFC before comparing.
Unicode 规范化。字符 "é" 可以是单个码点(U+00E9)或 "e" + 组合锐音符(U+0065 U+0301),取决于写法。它们的 UTF-8 百分号编码形式不同,朴素的相等比较会失败。比较前先规范化为 NFC。
Hashes and reserved characters. The fragment identifier (everything after #) is never sent to the server, so it does not need to be percent-encoded. But if you build a hash URL in JavaScript, you must encode the value before assigning to location.hash, otherwise the browser will truncate at the first #.
哈希与保留字符。片段标识符(# 之后的所有内容)从不上传到服务端,因此不需要百分号编码。但在 JavaScript 中构造带 hash 的 URL 时,必须先对值编码再赋给 location.hash,否则浏览器会在第一个 # 处截断。
Encoding by hand (for fun, not production)
手动编码(好玩,但不建议在生产中用)
If you ever need to encode or decode by hand, follow the spec (RFC 3986):
如果你需要手动编码或解码,请遵循规范(RFC 3986):
- Convert the character to its UTF-8 byte sequence.
- 将字符转换为 UTF-8 字节序列。
- For each byte, write
%followed by two uppercase hex digits. - 对每个字节写
%后跟两位大写十六进制数。
For example, "中" → UTF-8 bytes E4 B8 AD → %E4%B8%AD. The spec says uppercase, but most clients accept lowercase too. Be consistent.
例如,"中" → UTF-8 字节 E4 B8 AD → %E4%B8%AD。规范要求大写,但大多数客户端也接受小写。保持一致即可。
Quick reference
速查表
- Space →
%20(or+in query strings) - 空格 →
%20(查询字符串中也可用+) &→%26=→%3D?→%3F#→%23/→%2F+→%2B%→%25
Bottom line
总结
URL encoding is straightforward once you know the rules: encode the bytes, use the right function for the context, and never encode twice. The URL encoder on ToolHub runs entirely in your browser, so you can experiment with real input without risk.
URL 编码一旦掌握规则就很简单:编码字节、根据上下文选对函数、永远不要重复编码。ToolHub 的 URL 编码工具 完全在浏览器中运行,你可以放心地拿真实输入做实验。