JaguarVPN logo

URL Encoder and Decoder

Percent-encoding in all three flavours, side by side, so you can see which one you want.

Three rules, and picking the wrong one is the bug

Percent-encoding replaces a character with % followed by its byte value in hex. Simple enough. What causes trouble is that there are three different opinions about which characters need replacing, and they disagree on exactly the characters that matter.

RuleEscapes / ? & = #Space becomesUse for
encodeURIComponentYes%20A value going into a URL
encodeURINo%20Tidying a complete URL
Form encodingYes+What an HTML form sends

The tool shows all three at once for exactly this reason. Reading them side by side is faster than deciding in the abstract which one you meant.

The mistake this prevents

Suppose a search value is coffee & cake. Encoded as a component it becomes coffee%20%26%20cake, and the URL is:

/search?q=coffee%20%26%20cake

One parameter, value intact. Encoded with encodeURI, the ampersand survives as itself:

/search?q=coffee%20&%20cake

Now the server sees two parameters — q is “coffee ” and there is a second, empty one called “ cake”. Nothing errors. The search just quietly returns the wrong results, and this is the kind of bug that survives review because the URL looks fine.

The + that is not a plus

The other reliable source of confusion. In form-encoded data a + means a space, and a literal plus sign has to be %2B. So decoding C%2B%2B with the form rule gives C++, but decoding C++ gives C — two spaces, and the language name is gone.

This bites hardest on phone numbers. +44 20 7946 0958 submitted through a form and decoded with the wrong rule becomes 44 20 7946 0958, losing the country-code prefix, and nothing anywhere reports an error.

What to do instead of encoding by hand

In JavaScript, build query strings with URLSearchParams rather than concatenating. It applies the right rule per value and handles repeated keys:

const url = new URL('https://example.com/search');
url.searchParams.set('q', 'coffee & cake');
url.searchParams.set('page', '2');
// https://example.com/search?q=coffee+%26+cake&page=2

Every other language has an equivalent, and every one of them is more reliable than deciding character by character. This page is for the times you are reading a URL somebody sent you, or debugging one that already went wrong.

Reading an encoded URL

A few sequences are worth recognising on sight, because they turn up constantly in logs:

SequenceCharacterWhy it matters
%20spaceThe most common by far
%2F/An encoded slash in a path is often a traversal attempt
%3A:Common in encoded URLs inside redirect parameters
%25%Double-encoding shows up as %2520
%00nullAlmost never legitimate

%2520 in particular is worth knowing: it is %20 that has been encoded a second time. It usually means a value passed through two layers that each encoded it, and it is the signature of a redirect chain or proxy doing the wrong thing.

Related

For encoding bytes rather than escaping text, Base64 is the different job. If the decoded value turns out to be JSON, the formatter will read it, and if it is a token, the JWT decoder will.

Encoding questions

What is the difference between encodeURI and encodeURIComponent?

encodeURIComponent escapes the characters that have structural meaning in a URL — / ? & = # : and others — because it assumes what you are encoding is a value that will sit inside a URL. encodeURI leaves those alone, because it assumes you are handing it a whole URL that needs tidying. Encoding a query parameter with encodeURI is the common mistake: an & inside the value survives and silently splits your parameter in two.

Why is a space sometimes %20 and sometimes +?

Both are correct in their own context. %20 is the general percent-encoding for a space and works anywhere in a URL. The + convention comes from application/x-www-form-urlencoded, the format HTML forms submit in, where + means space and a literal plus must be written %2B. So a space in a path is %20, and a space in submitted form data is +. Decoding form data with the wrong rule turns every plus sign in the user's text into a space.

I got a URI malformed error. What causes it?

A percent sign that is not followed by two hexadecimal digits. Usually it is a literal % in text that was never encoded — a discount code like "50% off" will do it, because %20 is read as an escape sequence and %off is not valid. A literal percent has to be written %25. The other cause is double-decoding: running decode twice on something encoded once eventually hits a % that was meant to stay.

Should I encode the whole URL or just the values?

Just the values, essentially always. Build the URL structurally and encode each parameter as you insert it — in JavaScript, URLSearchParams does this for you and is more reliable than encoding by hand. Encoding a complete URL only makes sense when you have been handed one that is already malformed and you are patching it up.

Does encoding a URL make it safe?

No. Percent-encoding is about syntax, not safety — it makes sure a value survives being placed in a URL without changing the URL's structure. It does nothing about where the URL points, and it is not a defence against injection: a value that has been correctly encoded for a URL is still dangerous if it is later inserted into HTML or SQL without the escaping appropriate to those.

Last reviewed . Found something out of date? Tell us.