What percent-encoding actually is
URLs have reserved characters — :, /, ?, #, &, = — that have structural meaning. A query string like ?name=ada&role=admin uses & to separate fields and = to bind keys to values. If a user's name is literally ada & eve, you can't paste it in as-is: the parser would see three fields instead of one.
RFC 3986 defines the fix: percent-encoding. Replace each problem byte with % followed by two hex digits representing the UTF-8 byte value. & becomes %26, a space becomes %20, and a literal % becomes %25.
Non-ASCII characters use the same rule, one percent-escape per UTF-8 byte. A single "é" becomes %C3%A9 because UTF-8 encodes it as the two bytes 0xC3 0xA9. An emoji like "🎉" becomes %F0%9F%8E%89 — four bytes, so four escape sequences.
encodeURI vs encodeURIComponent — the classic bug
JavaScript ships with two built-in URL encoders, and they do different things. The bug you'll see most often is someone using the wrong one.
encodeURI(full_url)— encodes a whole URL. It leaves reserved characters like: / ? # & =alone because they have structural meaning. Use this when you already have a complete URL and just want to clean up non-ASCII bits.encodeURIComponent(one_value)— encodes a single component (a query value, a path segment). It escapes everything that isn't a letter, digit, or one of- _ . ! ~ * ' ( ). Use this when you're assembling a URL from parts.
The rule of thumb: if you're building a URL by concatenation — `/search?q=${encodeURIComponent(query)}` — always use encodeURIComponent. If you use encodeURI by mistake, a user searching for a&b=c will silently break your URL because & and = won't be escaped.
This tool runs both modes — switch between "component" and "full URL" to see the difference on the same input. Our URL Parser is the companion for breaking apart a URL into its pieces.
Query strings: space as %20 vs space as +
In RFC 3986, a space is always %20. But in application/x-www-form-urlencoded bodies and many older query-string encoders, a space is +. Both are correct in their respective contexts, which is confusing.
- In a URL path: always
%20. A+in a path is a literal plus. - In a query string: both work, but
+is the form-encoded default. Browsers, server frameworks, andURLSearchParamsemit+for spaces. - In a fragment (
#...):%20. Same rules as paths.
If you're debugging a server that's receiving hello+world when you expected hello world, it's probably form-decoding a path component that should have been URL-decoded strictly. Switch the decoder mode and the value comes out right.
Double encoding: a common bug
If you see an encoded URL like %2520 in your logs, something encoded a space twice — first as %20, then re-encoded the % to %25, producing %2520. That almost always means a middle layer ran encodeURIComponent on a value that was already encoded.
The fix is to know exactly where encoding happens in your pipeline and encode once, at the outermost layer. If you're constructing a URL from user input, encode the input. If you're reading a value out of a URL, decode it once — your framework usually does this for you.
Related URL tools on CodeBoxTools
- URL Parser — breaks a URL into scheme, host, path, query params, and fragment, with each value already decoded.
- Slug Generator — turn page titles into URL-safe slugs without having to encode them at all.
- Base64 Decode & Encode — the URL-safe Base64 variant uses a similar "replace unsafe chars" strategy.
- HTML Encode / Decode — different escaping scheme for a different context; easy to confuse with URL encoding.