A link breaks because one space or one ampersand in a query value was sent
raw. You reach for an encoder, discover two JavaScript functions with
similar names, and now the bug depends on which one you picked. We ran both
functions on the same inputs and documented where their outputs diverge and
where decoding throws.
What percent-encoding replaces
The rule is short. Every byte outside the unreserved set, letters, digits,
dash, dot, underscore, tilde, becomes a percent sign followed by two
hexadecimal digits. One character can become several percent groups, because
encoding works on UTF-8 bytes. We verified the three cases people hit first:
input encoded
hello world hello%20world
Q&A session Q%26A%20session
café & crème? caf%C3%A9%20%26%20cr%C3%A8me%3F
The space becomes %20. The ampersand becomes %26. The é is two bytes in
UTF-8, so it becomes %C3%A9. The question mark becomes %3F. These mappings
are byte-level facts, so they are identical in every correct implementation.
The two functions and the one rule for choosing
Our tool shows both outputs at once for a reason. The labels in the code are
aggressive for encodeURIComponent and mild for encodeURI, and the difference
shows up the moment your input contains structure characters:
input: https://example.com/search?q=hello world&lang=en#nav
encodeURIComponent
https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%20world%26lang%3Den%23nav
encodeURI
https://example.com/search?q=hello%20world&lang=en#nav
The aggressive form encodes the colons, slashes, question mark, ampersands,
and hash, because it assumes your text is data. The mild form encodes only
the space, because it assumes your text is already a URL.
That assumption is the choosing rule. Use encodeURIComponent on each
parameter name and value before you assemble a query string. Use encodeURI
only when you have a complete, already-structured URL and want to clean up
stray unsafe characters. Encode values, never re-encode an assembled URL,
and never build a query string by concatenating raw user text.
The ampersand is the character that causes the most damage. Send
q=Q&A session unencoded and the server reads a second parameter named
A session with no value. Our first sample shows the fix: %26.
Double encoding, the bug that hides until production
The percent sign itself is not in the unreserved set, so it encodes to %25.
Run an encoder over text that is already encoded and every existing escape
grows a second layer. We verified both directions:
already encoded Q%26A session %20
encoded again Q%2526A%20session %2520
decoded once Q%26A
The double-encoded string looks healthy and decodes without an error, which
is what makes it dangerous. The server receives the literal text %26 instead
of an ampersand and every comparison against the expected value fails.
The rule is structural. Encode once, at the boundary where raw text becomes
a URL, and never re-encode a string that already contains percent groups.
If a value arrives containing %25 followed by two hex digits, decode it once
and inspect the result before you use it.
Decode errors are data, not noise
Decode mode runs decodeURIComponent on your paste. Two input classes throw
URIError with the message "URI malformed", and the tool catches the throw
and shows its invalid string message instead of output.
1. A stray percent sign. The string 100% does not decode, because % starts
an escape and 0% is not one.
2. A truncated multi-byte sequence. The fragment %E2%82 is two of the three
bytes of a euro sign, so it throws before producing partial text.
Both errors are useful. A throw tells you the paste came from a system that
already decoded once, or that the string was cut mid-copy. Fix the source,
then decode again.
The plus sign trap
Forms submit spaces as plus signs in the application/x-www-form-urlencoded
format. Percent-encoding does not. We verified that
decodeURIComponent("a+b") returns a+b with both plus signs intact.
If you decode a form-submitted value and the output still contains plus
signs where spaces belong, you decoded with the wrong family. Search query
parameters in URLs use %20. Reach for decodeURIComponent for URL work, and
use form-specific decoding when the byte stream came from an HTML form post.
One annoyance documented in the code
Switching between the Encode and Decode tabs clears the input. The state
reset is intentional, but it costs you your paste when you want to encode a
string and immediately decode the result to compare. Keep the source text in
a scratch buffer until you finish the round trip.
Steps to fix a broken URL
1. Isolate the failing part. Load the URL in a browser and read the value
the server actually received.
2. Identify the character class in that value: space, ampersand, hash,
non-ASCII text.
3. Encode each parameter value with encodeURIComponent and reassemble the
query string.
4. Decode the result with decodeURIComponent and compare against your
source text. A throw means step 3 skipped a value.
5. Check for plus signs in the decoded output. Any survivor means the data
passed through form encoding somewhere upstream.
Checklist before you ship a URL with user data
- Every parameter value encoded with encodeURIComponent.
- Ampersands inside values sent as %26, never raw.
- Spaces sent as %20, and plus signs only where form encoding is explicit.
- Non-ASCII characters expected as multi-byte %C3%A9 style groups.
- A decode round trip applied once as a test.
If you have a URL that one encoder fixes and the other breaks, the pair of
outputs is worth keeping as a regression case. Compare the aggressive and
mild forms for your own strings in the URL encoder at
https://webrecast.com/en/url-encoder