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 − _ . ~

CharEncodedWhy
space%20 (+ in forms)breaks parsing
#%23starts fragment
&%26param separator
=%3Dkey-value separator
%%25the escape char itself
%E4%B8%ADUTF-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

  1. Encoding the whole URL — encodeURIComponent("https://a.com/?q=1") breaks everything; encode values, assemble structure.
  2. Forgetting path vs query rules — / is legal (structural) in paths but must be %2F inside a path SEGMENT value (user names with slashes).
  3. Assuming servers agree — some frameworks auto-decode once, some twice; know your stack's behavior before pre-decoding.
  4. IDN confusion — international domains use punycode (xn--fiq228c.com for 中文.com), not percent-encoding — different layer entirely.
  5. 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.