Core answer: URL encoding (percent-encoding) replaces unsafe characters with %XX hex bytes: space → %20, # → %23, % → %25, Chinese "中" → %E4%B8%AD (its 3 UTF-8 bytes). Reserved characters keep their meaning: ? starts the query, & separates params, = assigns, # starts the fragment. Encode component VALUES, never the whole URL — encoding the ? and & destroys the structure.
The reserved vs safe lists
Reserved (structural — encode inside values): : / ? # [ ] @ ! $ & ' ( ) * + , ; =
Unreserved (never encode): A–Z a–z 0–9 − _ . ~
| Char | Encoded | Why |
|---|---|---|
| space | %20 (+ in forms) | breaks parsing |
| # | %23 | starts fragment |
| & | %26 | param separator |
| = | %3D | key-value separator |
| % | %25 | the escape char itself |
| 中 | %E4%B8%AD | UTF-8 bytes E4 B8 AD |
+ vs %20: the form-data quirk
application/x-www-form-urlencoded encodes space as +; raw URLs use %20. A "+" in a query VALUE must be %2B, or servers read it as a space. This 40-year-old wart still bites APIs daily.
Worked examples
Example 1 — Search query. "C++ 教程" as a q parameter: q=C%2B%2B%20%E6%95%99%E7%A8%8B — the + signs encoded as %2B, space as %20, Chinese as UTF-8 bytes.
Example 2 — The double-encoding bug. Encoding "a b" twice: %2520 (the % of %20 got encoded to %25). If users see %25xx in your logs, something encoded twice — encode once, at the boundary.
Example 3 — Passwords in redirects. A redirect URL containing ?next=/dashboard?tab=2 must encode the inner URL: ?next=%2Fdashboard%3Ftab%3D2 — or the outer parser eats the inner query.
Example 4 — decodeURIComponent errors. URIError: malformed URI — input had a lone % (like "100% off" unencoded). Server-side: always wrap decode in try/catch; client-side: validate before decoding.
Common mistakes and myths
- Encoding the whole URL — encodeURIComponent("https://a.com/?q=1") breaks everything; encode values, assemble structure.
- Forgetting path vs query rules — / is legal (structural) in paths but must be %2F inside a path SEGMENT value (user names with slashes).
- Assuming servers agree — some frameworks auto-decode once, some twice; know your stack's behavior before pre-decoding.
- IDN confusion — international domains use punycode (xn--fiq228c.com for 中文.com), not percent-encoding — different layer entirely.
- Security blind spots — WAFs and filters can be bypassed with double-encoded payloads (%2527 → decodes to %27 → decodes to '); security layers must decode fully before matching.