Percent-Encoding Explained

URL encoding — also called percent-encoding — is how text is made safe to travel inside a web address: every character a URL cannot carry is replaced by a % sign followed by two hexadecimal digits, so a space becomes %20 and é becomes %C3%A9. It looks simple, yet the details break real links every day: a + that should have been %2B, a value encoded twice into %2520, an & that silently splits a query in two.

TL;DR — the short answer

Paste your text into the in-browser URL encoder and decoder: it converts as you type and nothing is uploaded. The rules in one breath: encode single values with component encoding (space → %20); + means space only in form data, never in a path; encodeURIComponent is for values, encodeURI for whole URLs; non-English text is encoded as UTF-8 bytes; and %2520 in a URL means something was encoded twice.

What is URL encoding (percent-encoding)?

A URL can only contain a limited set of ASCII characters, because certain characters are reserved as structure: / ? & = # separate its parts. RFC 3986 sorts characters into classes, with one rule for anything outside them: write the byte as % plus its two-digit hexadecimal value — the % sign itself becomes %25:

Class (RFC 3986)CharactersRule
UnreservedA–Z a–z 0–9 - . _ ~Never need encoding, in any part of a URL
Reserved — gen-delims: / ? # [ ] @Structural delimiters — encode when they are data, not structure
Reserved — sub-delims! $ & ' ( ) * + , ; =Allowed inside components — encode when a parser would misread them
Everything elseSpace, %, quotation marks, angle brackets, backslash, control characters, all non-ASCIIAlways encode — non-ASCII as UTF-8 bytes (see below)

The practical reading: reserved characters need encoding only when they are data rather than structure — an & inside a search term must become %26, while the & separating two parameters must not. Get the small details right, too — a colon encodes to %3A; some reference tables online wrongly print %3D, which is actually the code for =.

Why is a space %20 in a path but + in a form?

Because two different rules meet in the query string. Everywhere in a URL, a space's percent-encoded form is %20. But HTML forms submit data as application/x-www-form-urlencoded, an older format in which a space is written as +. Servers parsing form submissions — and most query strings along with them — read + as a space.

In a path, no such rule exists: + is an ordinary allowed character, a literal plus sign. /files/hello+world.txt names a file literally called hello+world.txt; /files/hello%20world.txt names hello world.txt. The mirror trap: a real plus sent as data must be %2B — an unencoded C++ in a form search reads back as “C” plus two spaces.

This is also why decoding surprises people: JavaScript's decodeURIComponent never converts + to a space, in any context. Correct form decoding replaces + with a space first, then percent-decodes — the order matters, or a genuine %2B would be corrupted. One more distinction: form encoding also escapes ~ ! ' ( ), leaving only A–Z a–z 0–9 and * - . _ untouched.

What is the difference between encodeURI and encodeURIComponent?

JavaScript ships two encoders because URLs hold two kinds of content: the structure, and the values inside it. encodeURI is for a complete, already-formed URL — it preserves the structural characters. encodeURIComponent is for one value you are inserting — it encodes those same characters so they cannot be mistaken for structure:

encodeURIencodeURIComponent
Leaves unescapedA–Z a–z 0–9 - _ . ! ~ * ' ( ) plus ; / ? : @ & = + $ , #A–Z a–z 0–9 - _ . ! ~ * ' ( ) only
Right choice forA whole URL, already built correctlyA single query value, path segment or fragment you are inserting

MDN's canonical bug shows the cost of choosing wrong: encodeURI("https://example.com/?choice=Ben & Jerry's") leaves the & untouched, so the server reads two parameters. Note also what neither function does: both leave ! ' ( ) * unescaped even though RFC 3986 reserves them — so neither output is strict RFC 3986, and neither produces form encoding. Decoding mirrors the split: decodeURI will not decode escapes for the structural characters; decodeURIComponent decodes all.

How is non-English text encoded in a URL?

In two steps: the text is first written as UTF-8 bytes, then every byte that is not an unreserved ASCII character becomes a %XX triplet. One character can therefore produce several triplets:

  • é (U+00E9) is two UTF-8 bytes, C3 A9 → %C3%A9 — so café becomes caf%C3%A9.
  • اردو (Urdu for “Urdu”) is four Arabic letters, two bytes each → %D8%A7%D8%B1%D8%AF%D9%88.
  • 😀 (U+1F600) is four bytes, F0 9F 98 80 → %F0%9F%98%80.

The pattern: one byte for ASCII, two for most European and Middle-Eastern scripts, three for CJK, four for emoji. One exception — international domain names use Punycode instead; percent-encoding applies to the path, query and fragment.

What is double encoding — and how do you fix %2520?

Encode an already-encoded string and the % signs themselves get encoded: % becomes %25, so %20 turns into %2520 and hello world becomes hello%2520world. The server decodes once, gets %20 back, and stores the still-encoded text — links break, searches miss, tokens fail.

It usually happens in layers — a framework auto-encodes a value you already encoded, or a redirect chain encodes the redirect= parameter again at every hop. Detection is a strong hint, not proof: %25 followed by two hex digits suggests double encoding, but the literal text “%20” legitimately encodes to %2520, so a careful tool flags it and offers to decode one more level rather than silently “fixing” it. Prevention is simpler: keep values raw and encode exactly once, at the boundary.

Why does decoding fail with an error?

Strict decoders such as decodeURIComponent throw a URIError — rightly, because some inputs have no correct decoding:

  • A % not followed by two hex digits — %ZZ, a truncated %2, or a trailing % at the end of the string.
  • Escapes that do not form valid UTF-8 — %C3 on its own (half of é), %C3%28, or %FF, a byte that can never appear in UTF-8. There is no character these decode to — anything a tool prints anyway is a guess.
  • A lone surrogate — half of an emoji pair left over from sloppy string slicing — makes the encoders throw, too.

Your browser's address bar is more forgiving — it passes a bad % through literally and swaps invalid bytes for � — which is why broken URLs can seem to “work” in the browser yet fail in code. When a decoder rejects input, a useful report names the position and the offending sequence; silent no-ops and boxes that just turn red tell you nothing. ToolsOfWeb's URL encoder and decoder pinpoints the bad sequence and its position instead.

Is it safe to paste URLs into an online encoder?

Look at what flows through URLs: API keys in query strings, session tokens, password-reset links, signed download URLs, OAuth callbacks. Percent-encoding is arithmetic your browser does natively in microseconds — yet several popular encoder sites send whatever you paste to their server to do it.

A browser-local tool removes the question entirely — and the claim is verifiable: open your Network tab while converting and you will see zero requests leave the page. Every ToolsOfWeb utility works this way — the Base64 converter and the JWT decoder — URLs and tokens are exactly the kind of data that should never leave the tab. Encode and decode your next URL with the free URL encoder and decoder.

FAQs

What is URL encoding?+

URL encoding, or percent-encoding, is the system for putting characters a URL cannot carry into a web address: each unsafe byte is written as % plus two hexadecimal digits — a space becomes %20, & becomes %26. Defined by RFC 3986, it keeps URLs valid and readable while arbitrary text — searches, names, non-English words — travels inside them.

What does %20 mean in a URL?+

%20 is a space. The space character’s byte value is 0x20, and percent-encoding writes any escaped byte as % followed by its hex value — so hello%20world is the text “hello world”. The 20 is the space’s ASCII code (32) in hexadecimal.

Why is space %20 in URL?+

Because a URL cannot contain a literal space — spaces are not in the allowed character set, and many systems would truncate the address at the gap. %20 is the space’s percent-encoded form and is understood everywhere in a URL: path, query and fragment. The + you sometimes see instead is a separate convention that only applies to form data.

What is the difference between encodeURI and encodeURIComponent?+

encodeURIComponent encodes everything except A–Z a–z 0–9 - _ . ! ~ * ’ ( ) — including the structural characters : / ? & = + $ , # — so it is for a single value going into a URL. encodeURI leaves those structural characters intact as well, making it safe only for a complete URL you have already built correctly.

What characters must be URL encoded?+

Anything that is not unreserved, whenever it appears as data. The unreserved set A–Z a–z 0–9 - . _ ~ never needs encoding. Encode the reserved characters (& = + / ? # and the rest) when they are part of a value rather than URL structure, the % sign itself (%25), spaces, quotes and brackets — and every non-ASCII character, as UTF-8 bytes.

How to encode a URL in JavaScript?+

Use encodeURIComponent(value) for each value you insert into a URL, and encodeURI(url) only for a whole, already-built URL; decode with decodeURIComponent and decodeURI. The equivalents elsewhere: Python’s urllib.parse.quote (quote_plus for form data) and PHP’s rawurlencode — urlencode is its form variant, which also uses + for spaces.

Is URL encoding the same as Base64?+

No. Percent-encoding escapes only the few characters a URL cannot carry and leaves the rest readable: hello world becomes hello%20world. Base64 recodes the entire input into a 64-character alphabet, growing it by about a third, and standard Base64 output contains + and /, themselves unsafe in URLs — hence the separate Base64URL variant. Different tools for different jobs.

Why does my decoded text show + instead of spaces?+

Because the text was form-encoded, where + means space — and decodeURIComponent never converts + back, in any context. Correct form decoding replaces + with a space first, then percent-decodes; the order matters, because a genuine %2B in the input must survive decoding as a literal +.

When should I use + instead of %20 for spaces?+

Only in application/x-www-form-urlencoded data: HTML form submissions and form-parsed query strings. Everywhere else — paths, fragments, values a generic URL parser will read — use %20, because there + is a literal plus sign, not a space. If in doubt, %20 is understood in both places; + is not.

Is what I paste sent to a server?+

It depends on the tool — and it matters, because URLs routinely carry API keys, session tokens, password-reset links and signed download URLs. Several popular encoders process your text on their server by default, for a job your browser does natively. A browser-local tool sends nothing: open the Network tab while converting and confirm zero requests.

For more free utilities, see ToolsOfWeb's Base64 Encoder & Decoder guide and JWT Decoder guide.