You build a search URL and it works fine in English, then breaks the moment a user types anything interesting:

https://example.com/search?q=书签管理 & page=2

The server sees q=书签管理 and a stray parameter called page. The & that was supposed to be part of the query value has been read as a separator. The same thing happens with #, =, ?, + and spaces.

The fix is percent-encoding (URL encoding): replace unsafe characters with % followed by two hexadecimal digits representing the UTF-8 byte.

书签管理  →  %E4%B9%A6%E7%AD%BE%E7%AE%A1%E7%90%86
&        →  %26
空格     →  %20

Which characters are safe?

RFC 3986 divides characters into unreserved and reserved. Only unreserved characters may appear literally:

Class Characters Notes
Unreserved A–Z a–z 0–9 - _ . ~ Safe anywhere
Reserved (gen-delims) : / ? # [ ] @ Structural meaning in a URL
Reserved (sub-delims) ! $ & ' ( ) * + , ; = Meaningful inside query strings
Other space, ", <, >, %, {, }, 中文… Must always be encoded

Reserved characters aren’t “forbidden” — they’re meaningful. The question is always: do I mean this & as structure, or as data? If it’s data, encode it.

encodeURI vs encodeURIComponent

JavaScript ships two built-in encoders and they behave differently:

const raw = 'https://example.com/search?q=书签 & page=2'

encodeURI(raw)
// https://example.com/search?q=%E4%B9%A6%E7%AD%BE%20&%20page=2
//        keeps : / ? & = intact — designed to encode a WHOLE URL

encodeURIComponent(raw)
// https%3A%2F%2Fexample.com%2Fsearch%3Fq%3D%E4%B9%A6%E7%AD%BE%20%26%20page%3D2
//        escapes : / ? & = too — designed to encode a VALUE

The rule of thumb:

  • Encoding a complete URL (that you built yourself)? Use encodeURI.
  • Encoding a parameter value that will be inserted into a URL? Use encodeURIComponent.

Most bugs come from using the wrong one. If your query string contains a nested URL, encodeURIComponent is almost always what you want.

Spaces: %20 or +?

Both appear in the wild, and the difference is historical:

  • The path and the standard say %20.
  • application/x-www-form-urlencoded (HTML forms, many query parsers) encodes spaces as +.

A + in a path segment is a literal plus, not a space. That’s why “why does my + turn into a space?” is such a common question: the parser is treating input as form-encoded data.

Why decoding fails

decodeURIComponent('%E4%B9%A6') works. decodeURIComponent('%E4%B9') throws, and decodeURIComponent('100%') throws too, because a lone % is not a complete escape sequence.

If you see a URIError, look for:

  1. Truncated sequences — a % without two hex digits after it.
  2. Double encoding%25E4... decodes once to %E4...; you need to decode twice (a sign that something upstream encoded twice).
  3. Wrong layer — decoding something that was never encoded, e.g. a raw % in a discount code.

How to encode or decode in your browser

The URL encoder and decoder runs entirely in your browser — no upload, works offline.

  1. Choose Encode or Decode.
  2. Paste the text, URL or encoded string.
  3. Press Convert (or Ctrl/ + Enter).
  4. Copy the result into your code or address bar.

It uses encodeURIComponent / decodeURIComponent, which is the behaviour you want for query parameter values.

FAQ

Should I encode the whole URL or just the parameter? Just the parts that are data. Build the structure yourself, then encode each value with encodeURIComponent.

Why does %20 sometimes appear as +? Form-encoded data uses + for spaces. Convert between them when talking to parsers that expect the other convention.

Do I need to encode Chinese characters? Yes. They are not ASCII, so they must be percent-encoded as UTF-8 bytes.

Can I decode a URL by hand? You can read a percent table, but for anything longer than a token, use the tool.