The broken link problem
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:
- Truncated sequences — a
%without two hex digits after it. - Double encoding —
%25E4...decodes once to%E4...; you need to decode twice (a sign that something upstream encoded twice). - 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.
- Choose Encode or Decode.
- Paste the text, URL or encoded string.
- Press Convert (or
Ctrl/⌘+Enter). - 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.
Related
- Base64 encoding explained — the other encoding you meet every day
- All NavProject tools — free, no upload, offline-capable
- NavProject — organize the links you keep coming back to