Calci.inCalculate Today for a Better Tomorrow

URL Encoder & Decoder

Percent-encoding for values or whole URLs

URL Encoder & Decoder details

Direction
Encode as

A single value escapes & / ? = # as well. A whole URL leaves them alone so the address still works. A form field is the same as a single value except that a space becomes + rather than %20 — which is what an HTML form actually sends.

The guide

URL encoding, and the two functions people confuse

Why a space becomes %20, when encodeURI is wrong and encodeURIComponent is right, and the double-encoding bug that follows from getting it backwards.

Last reviewed · 1,373 words

In short

  • Percent encoding replaces unsafe characters with a % and their hexadecimal byte value. A space becomes %20.
  • encodeURIComponent is what you want almost always. encodeURI leaves reserved characters alone and is only for whole URLs.
  • Encoding an already-encoded string produces %2520 instead of %20, and the resulting URL silently points somewhere else.
  • The plus sign means a space in query strings and a literal plus in paths. This inconsistency is historical and permanent.
  • Non-ASCII characters are encoded as UTF-8 bytes, so a Devanagari character becomes three percent-escapes.

A URL can only contain a limited set of characters. Everything else has to be encoded, and the mechanism is percent encoding: replace the character with a % followed by the hexadecimal value of each byte.

A space is byte 0x20, so it becomes %20.

What has to be encoded, and why

Three groups of characters cause trouble.

Reserved characters have a structural meaning in a URL: : / ? # [ ] @ ! $ & ' ( ) * + , ; =. A ? starts the query string, a # starts the fragment, an & separates parameters. When one of these appears as data rather than as structure, it must be encoded or it will be read as structure.

Unsafe characters — space, quotes, angle brackets, braces, backslash, caret, backtick, pipe — cause problems in transmission, in email clients that break long URLs across lines, and in software that has to guess where a URL ends.

Non-ASCII characters cannot appear in a URL at all. They are converted to UTF-8 bytes first, and each byte is percent-encoded.

Unreserved characters — letters, digits, - . _ ~ — never need encoding and should never be encoded.

The two JavaScript functions

This is the distinction that causes most real bugs.

encodeURIComponent encodes everything except unreserved characters. Use it for a piece of a URL: a query parameter value, a path segment, a fragment.

encodeURI leaves the reserved characters alone, because it assumes you are handing it a complete URL whose structure should survive.

The difference in practice:

InputencodeURIComponentencodeURI
hello worldhello%20worldhello%20world
a/ba%2Fba/b
a?b=ca%3Fb%3Dca?b=c
a&ba%26ba&b

Consider building a search URL for the query cats & dogs:

?q= + encodeURIComponent(cats & dogs) gives ?q=cats%20%26%20dogs — correct, one parameter.

?q= + encodeURI(cats & dogs) gives ?q=cats%20&%20dogs — the & survives as structure, so the server sees a parameter q with the value cats and a second parameter named with no value.

The rule: use encodeURIComponent for values, encodeURI for whole URLs, and in practice you almost always want the first.

Double encoding

Encoding a string that is already encoded is the second most common bug, and it is quiet.

%20 encoded again becomes %2520, because the % itself gets encoded to %25. Decoding once gives %20 back — a literal percent-two-zero as text, not a space.

It happens when a value is encoded in one layer of an application and encoded again in another, which is easy when a URL is built in one place and passed through a helper somewhere else that assumes raw input.

Symptoms: %2520 visible in the address bar, spaces appearing as literal %20 in page content, a redirect landing on a URL that looks nearly right.

The fix is to establish exactly one place responsible for encoding and to pass raw values everywhere else. Decoding twice to "fix" it is a workaround that breaks on any value containing a genuine %.

Plus signs and spaces

A space can be %20 or +, and which one applies depends on where in the URL it is.

In a query string, + means a space. This comes from the application/x-www-form-urlencoded format used by HTML forms since the beginning of the web.

In a path, + is a literal plus sign.

So /search?q=a+b searches for "a b", and /files/a+b.txt refers to a file with a plus in its name.

The consequence: a literal plus in a query value must be encoded as %2B, or it will be read as a space. This is the reason phone numbers in URLs lose their leading plus, and email addresses using the user+tag@example.com convention break in password reset links that were not built carefully.

%20 is valid everywhere and is the safer choice when building a URL yourself.

Non-ASCII and internationalised domains

A character outside ASCII is encoded as its UTF-8 bytes, each percent-escaped. The Devanagari character न is three bytes in UTF-8, so it becomes %E0%A4%A8 — three escapes for one character.

This is why a URL containing an Indian-language phrase looks enormous when encoded, and it is correct.

Domain names work differently. They cannot use percent encoding, so internationalised domains use Punycode: भारत.com becomes xn--h2brj9c.com. The xn-- prefix marks it.

Browsers display the readable form and transmit the Punycode. The security concern is the homograph attack — Cyrillic "а" looks identical to Latin "a", so a lookalike domain can be registered. Browsers now display Punycode rather than the readable form when a domain mixes scripts suspiciously.

Where encoding matters

Query parameters with user input. Search terms, filter values, anything typed by a person. Always encodeURIComponent.

Path segments containing data. A product name or a filename in a URL path needs encoding, particularly for slashes and question marks.

Redirect URLs passed as parameters. ?next=https://example.com/a?b=c needs the whole target encoded, or the outer parser will stop at the inner ?.

Anything constructed by string concatenation. The safest approach in modern JavaScript is the URL and URLSearchParams objects, which handle encoding correctly without you deciding — params.set('q', value) encodes the value properly whatever it contains.

Analytics and tracking parameters, which frequently carry URLs inside URLs and are a reliable source of double-encoding bugs.

Decoding, and where it goes wrong

Decoding is mechanically simpler and has two failure modes worth knowing.

A malformed sequence. A % not followed by two hexadecimal digits is invalid, and decodeURIComponent throws a URIError rather than returning something approximate. Any code decoding user-supplied input needs to handle that, because a crafted URL will otherwise produce a server error.

Invalid UTF-8. Percent escapes decode to bytes, and those bytes must form valid UTF-8. A sequence that does not — frequently from text encoded in a legacy codepage such as ISO-8859-1 and then percent-escaped — throws for the same reason.

The second is why old links sometimes cannot be decoded at all. A URL built in 1998 with Latin-1 bytes is not valid UTF-8 today, and there is no general way to recover the intended characters without knowing the original encoding.

Query strings, and the parser you should use

Building a query string by concatenating pieces is where most encoding bugs enter a codebase.

The modern answer in JavaScript is URLSearchParams, which encodes each value correctly without any decision from you and handles repeated keys, empty values and ordering consistently. new URL() does the same for whole URLs, including resolving a relative path against a base.

Server frameworks have equivalents, and every one of them is more reliable than string concatenation.

Two details worth knowing about query parsing. Repeated keys — ?a=1&a=2 — have no standard behaviour: some parsers keep the first, some the last, and some produce an array. And an empty value — ?a= — is distinguishable from an absent key by most parsers and not by all, which occasionally matters for a filter that means "match empty" rather than "no filter".

Encoding in other contexts

Percent encoding is one of several escaping schemes, and using the wrong one for the context is its own class of bug.

HTML entity encoding protects characters that have meaning in markup: &, <, >, ". A URL placed inside an HTML attribute needs both — percent encoding for the URL structure and entity encoding for the attribute — which is why an ampersand in a link href is correctly written & in the HTML source and still means a single & in the URL.

JavaScript string escaping uses backslashes and is different again.

SQL escaping is different again, and the correct answer there is not to escape at all but to use parameterised queries.

The general principle: escape at the boundary, for the destination, and only once. A value that will be placed in a URL inside an HTML attribute rendered by a template engine passes three boundaries, and each needs its own encoding applied by whatever handles that layer. Doing it manually at the wrong point is how both double-encoding and injection vulnerabilities arise.

What this tool assumes

  • Encoding uses percent encoding with UTF-8 for non-ASCII characters, matching current standards.
  • The component mode encodes reserved characters; the full-URL mode preserves them.
  • Decoding a malformed sequence — a % not followed by two hexadecimal digits — is reported rather than guessed at.
  • Plus signs are treated as literal on encode; decode mode offers both interpretations, because the correct one depends on context.
  • Everything runs in your browser. Nothing you paste is sent anywhere.

Sources

Frequently asked questions

What is the difference between encodeURI and encodeURIComponent?

encodeURI leaves the characters that structure a URL — : / ? & = # — intact, so a whole address survives. encodeURIComponent escapes those too, which is what a single parameter value needs.

Why does my query parameter break?

Almost always because a value contained an ampersand or an equals sign and was encoded with the wrong function. The server then reads it as the start of a new parameter.

Why is a space sometimes %20 and sometimes +?

%20 is standard percent-encoding. The plus comes from HTML form submission, which uses application/x-www-form-urlencoded. Both decode to a space, and this tool accepts either.

Do I need to encode non-English characters?

Yes, for compatibility. Browsers display Unicode URLs but transmit them percent-encoded as UTF-8, which is why a Hindi or Chinese word becomes a long run of escapes.