URL Encoder / Decoder
Percent-encode text for use in a URL, or decode an encoded URL back to readable text, with separate handling for full URLs and individual parameters.
A plus is a space, except where it is a plus
There are two encodings in daily use on the same URLs, and they disagree about one character.
Percent-encoding writes a space as %20 and treats + as an ordinary
character. Form encoding — the rule every query string built by an HTML form uses — writes a
space as +. So the same bytes decode two different ways, and both are correct:
| Encoded | As percent-encoding | As a form value |
|---|---|---|
a+b | a+b | a b |
hello+world | hello+world | hello world |
one%2Btwo | one+two | one+two |
a%20b | a b | a b |
C%2B%2B | C++ | C++ |
rock+%26+roll | rock+&+roll | rock & roll |
Neither decoder is buggy. decodeURIComponent implements percent-encoding;
URLSearchParams implements the form rule. Handing a query string to the wrong
one silently produces the wrong text, and it only surfaces when somebody's search term happens
to contain a plus or a space — which is to say, in production rather than in testing.
The greyed rows are the ones where both rules agree. Notice they are exactly the inputs with no
plus in them: the disagreement is about that single character and nothing else. The practical
rule is to parse query strings with URLSearchParams and use
encodeURIComponent for path segments and for values you assemble by hand —
and never to mix the two on one string.
Encoding twice is where %2520 comes from
Percent-encoding escapes the percent sign itself, so running it twice does not leave the string alone — it adds a layer.
| Passes | Result |
|---|---|
| none — the original | a b&c |
| 1 | a%20b%26c |
| 2 | a%2520b%2526c |
| 3 | a%252520b%252526c |
Every pass is reversible, and n encodes need exactly n decodes to undo. That is why a
double-encoded value looks fine right up until something decodes it once and passes on a string
that still has %20 in it. If you have ever seen %2520 in a real URL,
this is what happened: a space became %20, and then the % became
%25.
There is a sharper edge too. Decoding is not a total function — it throws on a malformed escape rather than passing the text through:
| Input | decodeURIComponent |
|---|---|
% | throws URIError |
%zz | throws URIError |
%E0%A4%A | throws URIError |
100% | throws URIError |
ok%20fine | ok fine |
A lone percent sign is enough. Any code that decodes user-supplied input needs a try/catch, or
it will crash the first time somebody types 100%.
Which function to use, and what each leaves alone
The difference between the two built-in encoders is not strictness, it is scope.
encodeURI is for a whole URL, so it keeps the characters that hold one
together. encodeURIComponent is for one piece of a URL, so it escapes them —
otherwise a value containing a slash or an ampersand would change the URL's structure.
| Character | encodeURIComponent | encodeURI |
|---|---|---|
space | %20 | %20 |
! | ! | ! |
' | ' | ' |
( | ( | ( |
) | ) | ) |
* | * | * |
+ | %2B | + |
/ | %2F | / |
? | %3F | ? |
& | %26 | & |
= | %3D | = |
# | %23 | # |
% | %25 | %25 |
: | %3A | : |
@ | %40 | @ |
One quirk worth knowing if you are implementing a signed request. Even
encodeURIComponent leaves
!'()* alone,
although RFC 3986 lists them as reserved sub-delimiters. Specifications that require strict
RFC 3986 encoding — OAuth 1.0 signing is the usual one — need those five escaped by hand, which
is why every OAuth library carries its own encoder rather than using the built-in.
How to use
- Paste text to encode or an encoded string to decode.
- Choose whether you are encoding a whole URL or one component.
- Copy the result.
- Encode components separately before assembling a URL.
Frequently asked questions
What is percent-encoding?
Replacing an unsafe character with a percent sign and its two-digit hexadecimal byte value. A space becomes %20, a question mark %3F. It exists because URLs may only contain a limited set of characters, and anything else has to be represented indirectly.
Why is a space sometimes %20 and sometimes a plus?
Two different rules. Percent-encoding proper uses %20 everywhere. The plus sign comes from HTML form submission, which uses a slightly different encoding for query strings. Both appear in the wild, so a decoder has to know which context it is in — mishandling this turns a plus in someone's data into a space.
When do I encode the whole URL versus one part?
Encode components separately, then join them. Encoding a complete URL escapes the slashes and colons that give it structure, producing something unusable. Encoding just the parameter value leaves the URL's skeleton intact — which is why most languages provide two separate functions for this.
Which characters must be encoded?
The reserved ones that carry structural meaning — question mark, ampersand, equals, slash, hash, plus — whenever they appear inside a value rather than as part of the URL's structure. An ampersand inside a parameter value will otherwise be read as the start of the next parameter.
Why do I sometimes see %2520?
Double encoding. The percent sign of an already-encoded %20 got encoded again into %25, leaving %2520. It usually means a value passed through two layers that each encoded it, and it surfaces as a literal %20 appearing in the page.
Does this handle non-English characters?
Yes. Characters outside ASCII are converted to UTF-8 bytes first and then percent-encoded, which is why a single accented character can become several percent groups. This is the modern standard and what browsers do.
🔒 This tool runs entirely in your browser. Nothing you enter is uploaded, logged, or stored.