Ever pasted a link and seen %E4%B8%AD where a word should be, or had a query parameter mysteriously break? That’s URL encoding at work. Once you understand it, those cryptic strings turn into something you can read and reason about.
One input, three lessons. We encoded
台北 101 & friends?with our URL encoder and got:%E5%8F%B0%E5%8C%97%20101%20%26%20friends%3FThree things fall out of that string. Each Chinese character became three bytes (
%E5%8F%B0), because UTF-8 needs three for CJK. The spaces became%20, not+— the plus form belongs to form bodies, not paths. And&and?were escaped precisely because unescaped they would end the value and start a new parameter. Paste the same text in and compare.
What is URL encoding?
URL encoding — also called percent-encoding — is a way to represent characters in a URL that either aren’t allowed there or would otherwise be misread. Each such character is replaced by a % followed by the two-digit hexadecimal value of its byte. A space becomes %20; an ampersand becomes %26; the Chinese character 中 (in UTF-8) becomes three bytes, %E4%B8%AD.
The rules come from the URL standard (RFC 3986). The goal is simple: a URL should survive being copied, emailed, and parsed by any server without ambiguity.
You can encode or decode any string instantly with a URL encoder / decoder — it runs locally in your browser.
Why URLs need encoding
A URL may only contain a limited set of ASCII characters. Anything outside that set — spaces, accented letters, non-ASCII scripts, or characters that carry special meaning like ?, &, #, / — has to be percent-encoded so it isn’t mistaken for URL structure.
Take ? as an example: in a URL it marks the start of the query string. If you want a literal question mark inside a value, you must write it as %3F, otherwise the parser treats everything after it as parameters. Encoding removes that ambiguity.
How common characters are encoded
Here are the symbols people run into most often, with their percent-encoded forms:
| Character | Encoded | Notes |
|---|---|---|
| space | %20 | + only in form data (see below) |
& | %26 | separates query parameters |
? | %3F | starts the query string |
= | %3D | splits a key from its value |
# | %23 | starts the fragment |
/ | %2F | path separator |
+ | %2B | literal plus, so it isn’t read as a space |
中 | %E4%B8%AD | non-ASCII, encoded per UTF-8 byte |
ä | %C3%A4 | non-ASCII, two UTF-8 bytes |
Non-ASCII text (Chinese, Japanese, emoji, accented Latin) is first turned into its UTF-8 bytes, then each byte is percent-encoded. That’s why one visible character can expand into several %XX groups.
Here is one string through our own URL encoder: 台北 101 & friends? becomes %E5%8F%B0%E5%8C%97%20101%20%26%20friends%3F. Three facts fall out of it — each Chinese character took three bytes, the spaces became %20 and not +, and & plus ? were escaped because unescaped they would end the value and start a new parameter.
Rather than reprint the RFC table, here is what our own URL encoder returned for eight inputs picked to hit every category at least once. Two of them come back untouched, and that is the interesting part: ~, _, . and - are unreserved, so a correct encoder must leave them alone.
| Input | Encoded |
|---|---|
hello | hello |
hello world | hello%20world |
a+b=c | a%2Bb%3Dc |
100% | 100%25 |
台北 | %E5%8F%B0%E5%8C%97 |
?q=x&r=y | %3Fq%3Dx%26r%3Dy |
a/b | a%2Fb |
~_.- | ~_.- |
Reserved vs. unreserved characters
- Unreserved characters (
A–Z a–z 0–9 - _ . ~) never need encoding. - Reserved characters (
: / ? # [ ] @ ! $ & ' ( ) * + , ; =) have structural meaning in a URL. Whether you encode them depends on context — inside a value they must be encoded; when they act as structure they must not.
This distinction is the heart of the encodeURIComponent vs. encodeURI choice below.
When do you need to encode?
The short answer: encode the parts you put into a URL, not the URL you already built.
- Encode a value you’re inserting — a search term, a filename, a redirect target, anything that came from user input or another system.
- Don’t blindly encode a whole, finished URL — that would escape the
/ ? & =that hold it together and break the link.
A useful mental model: build the URL structure yourself, and encode each piece before you drop it in. Encoding the parameter value red & blue gives red%20%26%20blue, which slots safely into ?color=red%20%26%20blue.
encodeURIComponent vs. encodeURI
encodeURIComponentencodes reserved characters too. Use it for a single value — one query parameter, one path segment.encodeURIleaves reserved structural characters (/ ? & =) intact. Use it for an entire URL you don’t want to break apart.
encodeURIComponent("a b&c=d") // "a%20b%26c%3Dd" ✅ safe as a value
encodeURI("https://x.com/a b") // "https://x.com/a%20b" ✅ whole URL
Picking the wrong one is a top source of bugs: run encodeURI on a value and the & inside won’t be escaped, corrupting your query string.
Encoding a space: %20 vs. +
Spaces trip people up more than any other character. Both %20 and + can represent a space, but in different contexts:
- In a URL path and modern query strings, the standard is
%20. - The
+for a space comes from the olderapplication/x-www-form-urlencodedformat used by HTML form submissions. There, a literal+has to be written as%2B.
When in doubt, %20 is the safe, universal choice — and it’s what our tool produces.
For contrast, the same text through our Base64 tool gives 5Y+w5YyXIDEwMQ==. Both start from the identical UTF-8 bytes and then diverge completely: Base64 re-expresses every byte in a 64-character alphabet and pads to a multiple of four, while percent-encoding leaves safe characters visible and escapes only the rest.
This is also where the + confusion begins. Our encoder returns a%2Bb%3Dc for a+b=c — the plus sign itself becomes %2B. It has to. If a literal + were left as-is, any decoder that reads + as a space would silently corrupt the value, and form decoders do exactly that. So +-means-space and +-means-plus can never coexist unencoded; the encoder settles it by never emitting a bare + at all.
The classic double-encoding bug
If a string is encoded twice, %20 becomes %2520 (because the % itself gets encoded to %25). Symptoms: literal %20 showing up in the browser, or spaces turning into %2520. The fix is to encode exactly once — decode first if you’re unsure of the input’s state. A decoder makes it easy to inspect what you actually have.
The same mechanism explains double encoding. % is not safe in a URL either — our encoder turns 100% into 100%25. Run that output through the encoder a second time and %25 becomes %2525: still a valid URL, still decodable, just decoding to the wrong string. Double encoding is never a syntax error, which is why it survives all the way to production.
How to encode or decode a URL online
You don’t need to memorize hex tables. Paste your text or URL into the online URL encoder / decoder, switch between encode and decode, and copy the result. It works entirely in your browser, so nothing you paste is uploaded — handy for links that contain tokens or personal data. For binary data or headers you’ll instead want Base64; the Base64 vs. URL encoding guide explains when to use which.
Common questions
Do I need to encode a normal-looking URL before sharing it? Usually no — if it already works in a browser, it’s fine. Encode only the values you’re assembling into a new URL.
Why did my & split my link in two? An unencoded & starts a new query parameter. Inside a value it must be %26.
Why is there %2520 in my link? That’s a double-encoded space. Decode once to recover the original %20.
Are %20 and + interchangeable? Only in x-www-form-urlencoded form bodies. In the path and general query strings, prefer %20.
Quick reference
- Encoding a parameter value? →
encodeURIComponent. - Encoding a whole URL? →
encodeURI. - Need a space? →
%20(or+only in form data). - See a stray
%XX? → decode it to read the original.
Encode and decode URLs and query parameters instantly with the free URL encoder / decoder — it runs locally in your browser.