← Back to all articles

URL Encoding: When You Need It, and Where %20 and + Come From

URLEncoding

Why encode at all

URLs only allow a limited character set. Chinese characters, spaces, &, =, ? and friends must be written as %XX sequences to travel safely — otherwise parsers mistake them for structural separators.

%20 vs +

Inside query strings (application/x-www-form-urlencoded) a space was historically written as +; in the URL path it can only be %20. Decoders disagree on how to treat +, which is where "my spaces turned into plus signs" bugs come from — prefer %20 for spaces in parameter values.

encodeURI vs encodeURIComponent

  • encodeURI: keeps URL structure characters (: / ? # [ ] @) — use it for a whole URL;
  • encodeURIComponent: encodes aggressively — use it for individual parameter values.

Applying encodeURIComponent to a full URL encodes the :/ too and breaks the link — the most common misuse.

Two roots of Chinese mojibake

  1. Asymmetric encoding: encoded twice but decoded once (or the reverse), leaving %XX residue;
  2. Charset mismatch: sent as UTF-8 but parsed as GBK on the server — guaranteed mojibake.

Common characters and their encoding

CharacterEncodedNotes
Space%20 or +Query strings historically use +; paths must use %20
Non-ASCII letters%E4%B8%ADEach UTF-8 byte becomes one %XX
&%26Otherwise parsed as a parameter separator
=%3DMust be escaped inside parameter values
#%23Otherwise truncates the URL as a fragment

Code examples

// Frontend: always encodeURIComponent when building parameters
const url = '/search?q=' + encodeURIComponent('a b & c');
// => /search?q=a%20b%20%26%20c

// Node: new URLSearchParams({ q: 'a b & c' }).toString()
// Python: urllib.parse.quote('a b & c', safe='')

Three debugging tips

  1. When unsure, decode once first to check whether the value was encoded twice;
  2. On the server, confirm whether the framework decodes + as a space — in a URL path it is a literal plus;
  3. For non-ASCII mojibake, verify that both ends use UTF-8 before re-encoding anything.

Real-world cases: three common failures

  1. "Spaces in my search term became +": the frontend encoded with form rules and the backend decoded with path rules. Use %20 for spaces in parameter values to avoid it.
  2. "Chinese parameters arrive garbled": usually a charset mismatch (sent UTF-8, parsed GBK). Unify on UTF-8 at both ends instead of encoding/decoding repeatedly.
  3. "%2520 shows up in the link": %20 was encoded twice into %2520 (the % became %25). Remove the duplicate encoding step.

FAQ

Can a path use + for a space? No — only query strings may treat + as space; in a path it is a literal plus. Can encodeURIComponent encode a whole URL? Not advisable; it encodes :/?&= too and breaks the structure. Why does one Chinese character encode to three %XX groups? Because UTF-8 uses three bytes for it, each percent-encoded: %E4%B8%AD. Does the browser encode Chinese in the address bar automatically? It displays it unencoded but still sends %XX, so when building URLs by hand, encode it yourself.

Try them: URL encoder/decoder, Base64 encoder/decoder

Conventions between frontend and backend

  1. Agree on who encodes: the frontend encodes once while building the link and the backend only decodes and validates — never both, or double encoding corrupts values.
  2. Treat names and values differently: parameter names are developer-defined and need no encoding; values come from users and must be encoded. Handling them together turns readable names into noise.
  3. Keep both forms in logs: log the decoded, readable content alongside the raw encoded string; logging only the encoded form turns debugging into manual decoding.
  4. Test boundary characters: spaces, plus signs, percent signs, Chinese text and emoji all break easily, so cover them in API tests rather than spot-checking by hand.
  5. Document the convention: state in the API docs whether a parameter expects an encoded or original value — one sentence saves hours of integration work.

Splitting encoding responsibility clearly and writing it into docs and tests is the durable fix for link-related bugs.

Interaction with gateways and proxies

When a request passes a reverse proxy or gateway, the path and query string may be rewritten. If a rule processes already-encoded percent signs again, values end up double-encoded or undecodable. Compare the address the client sent with the one the application received — the difference usually shows immediately which layer handled it one time too many.