To URL encode a string, you replace any character that is not allowed in a URL with a percent sign followed by its two-digit hexadecimal byte value — so a space becomes %20 and & becomes %26. To decode, you reverse the process: every %XX sequence is turned back into the original character. The fastest way to do this without writing code is to paste your text into the free URL Encoder/Decoder, which runs entirely in your browser.
URL encoding (more precisely, percent-encoding) is one of those topics that seems trivial until a stray ampersand breaks your API call or a double-encoded space shows up in a customer’s email link. This guide explains exactly what gets encoded and why, the difference between the JavaScript encoding functions, how query strings differ from path segments, and the double-encoding traps that bite even experienced developers.
What Is URL Encoding?
A URL has a strict grammar defined by RFC 3986. Only a limited set of characters is allowed to appear literally in a URL. Everything else — spaces, accented letters, emoji, and characters that have special structural meaning (like ?, &, #, and /) when they appear in the wrong place — must be escaped.
Percent-encoding works in three steps:
- The character is converted to its bytes using a character encoding (today, almost always UTF-8).
- Each byte is written as a two-digit uppercase hexadecimal number.
- Each hex pair is prefixed with a
%.
So the space character (byte value 0x20) becomes %20. The character é, which is two bytes in UTF-8 (0xC3 0xA9), becomes %C3%A9. Decoding simply runs the steps in reverse.
Reserved, Unreserved, and Everything Else
RFC 3986 splits characters into categories. Understanding these categories is the whole game:
| Category | Characters | Need encoding? |
|---|---|---|
| Unreserved | A–Z a–z 0–9 - _ . ~ |
Never — always safe |
| Reserved (gen-delims) | : / ? # [ ] @ |
Only when used as data, not structure |
| Reserved (sub-delims) | ! $ & ' ( ) * + , ; = |
Only when used as data, not structure |
| Everything else | space, " < > \ ^ ` { } ` |
`, accented letters, emoji, control chars |
The subtle part is the reserved set. A / is perfectly legal — as a path separator. But if a slash is part of a single path segment’s value (say, a filename literally named report/2024), it must be encoded as %2F, or the server will read it as two segments. The character is the same; the meaning is what changes.
When You Need to URL Encode
You reach for URL encoding any time user-controlled or arbitrary text has to travel inside a URL. Common situations:
- Building query strings —
?q=hello world&category=books & morewill break unless the values are encoded. - Path segments with special characters — slugs, IDs, or filenames containing spaces, slashes, or Unicode.
- Redirect parameters — passing one URL as a parameter of another (
?next=https://example.com/page?a=1) requires encoding the inner URL completely. - OAuth and signed requests — signature algorithms depend on a precisely defined encoding; one wrong character invalidates the signature.
- Form submissions — HTML forms with
application/x-www-form-urlencodedencoding (the default) percent-encode field names and values automatically.
If you are debugging any of these and just need to see the encoded or decoded version of a string, the URL Encoder/Decoder gives you the answer instantly — paste, click, copy.
How to URL Encode a String (Step by Step)
Method 1: Use the Online Tool (Fastest)
- Open the URL Encoder/Decoder.
- Paste your raw text into the input box.
- Click Encode to get the percent-encoded result, or Decode to reverse an encoded string.
- Copy the output.
Everything happens locally in your browser — nothing is uploaded to a server, which matters when your URLs contain tokens, internal hostnames, or anything sensitive.
Method 2: In JavaScript
The browser and Node.js give you two built-in functions:
encodeURIComponent("hello world & more");
// => "hello%20world%20%26%20more"
decodeURIComponent("hello%20world%20%26%20more");
// => "hello world & more"
Use encodeURIComponent for a single piece of data — one query value, one path segment.
const base = "https://api.example.com/search";
const term = "blue + green shoes";
const url = `${base}?q=${encodeURIComponent(term)}`;
// => "https://api.example.com/search?q=blue%20%2B%20green%20shoes"
Method 3: On the Command Line
curl can encode query data for you with --data-urlencode:
curl -G "https://api.example.com/search" \
--data-urlencode "q=blue + green shoes"
In Python, the standard library does it cleanly:
from urllib.parse import quote, quote_plus, unquote
quote("hello world & more") # 'hello%20world%20%26%20more'
quote_plus("hello world & more") # 'hello+world+%26+more' (form style)
unquote("hello%20world%20%26%20more") # 'hello world & more'
encodeURI vs encodeURIComponent
This is the single most common source of confusion in JavaScript, so it deserves its own section. Both functions percent-encode strings, but they treat the structural characters of a URL completely differently.
| Function | Leaves these unescaped | Use it for |
|---|---|---|
encodeURI() |
A–Z a–z 0–9 - _ . ! ~ * ' ( ) plus ; / ? : @ & = + $ , # |
An entire, already-structured URL |
encodeURIComponent() |
A–Z a–z 0–9 - _ . ! ~ * ' ( ) only |
A single component (query value, path segment) |
The practical difference: encodeURI assumes the string is a complete URL and deliberately leaves the delimiters (/, ?, &, =, #, etc.) alone so the URL stays usable. encodeURIComponent assumes the string is just a value that will be dropped into a larger URL, so it escapes those delimiters too.
const value = "a/b?c=d&e";
encodeURI(value);
// => "a/b?c=d&e" ← delimiters left intact (often WRONG for a value)
encodeURIComponent(value);
// => "a%2Fb%3Fc%3Dd%26e" ← everything escaped (correct for a value)
Rule of thumb: if you are inserting a piece of data into a URL, you almost always want encodeURIComponent. Reach for encodeURI only when you have a whole URL with spaces or Unicode in it and you want to make it valid without destroying its structure.
One caveat: neither function escapes !, ', (, ), or *. These are technically legal in URLs, but some stricter servers and the OAuth 1.0 spec want them encoded. If you need full compliance, post-process the output:
function strictEncode(str) {
return encodeURIComponent(str)
.replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase());
}
Query Strings vs Path Segments
Although percent-encoding looks the same everywhere, the rules about which characters are dangerous shift depending on where in the URL you are.
Path segments
In the path (/users/alice/files/report.pdf), the slash is the separator. If a value within a segment contains a slash, it must be encoded as %2F, and a space as %20. The + character has no special meaning in a path — it is a literal plus sign, so a literal space must never be written as + in a path.
Raw segment: my report (final).pdf
Encoded path: /files/my%20report%20(final).pdf
Query strings
In the query string (?key=value&key2=value2), the separators are ?, &, and =. Any of those characters appearing inside a key or value must be encoded (%3F, %26, %3D). And here is the historical wrinkle: the application/x-www-form-urlencoded format — used by HTML forms and many APIs — encodes a space as + rather than %20, and treats a literal + as %2B.
Raw value: blue + green
Form-encoded: q=blue+%2B+green
Plain-encoded: q=blue%20%2B%20green
Both forms decode to the same thing if the decoder knows the context. A query-string decoder turns + into a space; a path decoder leaves + as a literal plus. This is why decodeURIComponent("a+b") returns "a+b" (it does not touch +) while a form parser returns "a b". Mixing them up produces stray + signs or missing spaces in your data.
| Position | Space encodes as | + means |
|---|---|---|
| Path segment | %20 |
literal + |
| Query string (form-urlencoded) | + (or %20) |
a space, unless written %2B |
Common Mistakes and Pitfalls
1. Double encoding
The most frequent bug. You encode a value, then accidentally encode the whole URL again — or your framework encodes it once and you encode it a second time.
Original: hello world
Encoded once: hello%20world
Encoded twice: hello%2520world ← the % itself got encoded to %25
When you see %25 in a place where you expected %20, you have a double-encoding problem. Decode the string twice to confirm, then remove the duplicate encoding step in your code. Pasting the broken value into the URL Encoder/Decoder and decoding it once or twice is the quickest way to diagnose this — if it takes two decode passes to read, it was encoded twice.
2. Encoding an entire URL with encodeURIComponent
If you run a full URL through encodeURIComponent, the slashes and colons get escaped and the URL stops working as a link:
encodeURIComponent("https://example.com/path");
// => "https%3A%2F%2Fexample.com%2Fpath"
That output is correct only when the URL is itself a value (for example, a ?next= redirect parameter). As a standalone address it is broken. Use encodeURI (or nothing) for whole URLs.
3. Forgetting the character-set step
Percent-encoding operates on bytes, not characters. A non-ASCII character must first be turned into bytes via UTF-8. Encoding é as a single %E9 (its Latin-1 byte) instead of %C3%A9 (its UTF-8 bytes) leads to mojibake — garbled text — on any UTF-8 server. Modern functions like encodeURIComponent always use UTF-8, so prefer them over hand-rolled encoders.
4. Encoding + in form data by hand
If you manually build a form body and write a space as %20, most parsers accept it. But if you write a literal + expecting it to survive, it will be decoded as a space. Always encode a literal plus in form data as %2B.
5. Trusting decoded input
Decoding is where security bugs hide. A value like %2e%2e%2f decodes to ../, which can enable path-traversal attacks if you decode and then use the result as a file path. Always validate decoded input after decoding, and never decode a value more than once unless you can prove it was encoded that many times.
Worked Examples (Input → Output)
| Raw string | URL encoded |
|---|---|
hello world |
hello%20world |
a&b=c |
a%26b%3Dc |
100% |
100%25 |
[email protected] |
user%40example.com |
café ☕ |
caf%C3%A9%20%E2%98%95 |
path/to/file (as a value) |
path%2Fto%2Ffile |
?key=val#frag |
%3Fkey%3Dval%23frag |
To decode, simply read the table right-to-left: caf%C3%A9 becomes café once each percent-pair is converted back to its byte and the bytes are interpreted as UTF-8.
Quick Reference: Characters That Always Need Encoding
These never appear safely as data and should always be percent-encoded when they are not serving their structural role:
space → %20 (or + in form data)
" → %22
# → %23
% → %25
& → %26
+ → %2B (when it must stay a literal plus)
/ → %2F (inside a single path segment)
: → %3A
; → %3B
= → %3D
? → %3F
@ → %40
Encode and Decode in One Click
You do not need to memorize the hex tables. Open the URL Encoder/Decoder, paste your string, and switch between Encode and Decode. It handles UTF-8 and special characters, runs entirely in your browser (nothing is uploaded), and is free with no sign-up. It is the fastest way to verify a query string, untangle a double-encoded value, or sanity-check what your backend is actually receiving.
If you work with the data formats and tokens that travel through URLs, these related guides pair well with this one:
- How to Decode a JWT Token — JWTs use Base64URL, the URL-safe cousin of percent-encoding.
- How to Format JSON Online — clean up the JSON you decoded out of an API response.
- How to Convert CSV to JSON — another everyday data-wrangling task.
- How to Test Regex Online — handy for validating encoded patterns and slugs.
Frequently Asked Questions
What is the difference between URL encoding and Base64?
URL encoding (percent-encoding) makes a string safe to put inside a URL by replacing unsafe characters with %XX codes. Base64 converts binary data into 64 printable ASCII characters. They solve different problems and are not interchangeable, though both are sometimes used to make data “URL-safe.”
Should I use encodeURI or encodeURIComponent in JavaScript?
Use encodeURIComponent for individual pieces of a URL like a query parameter value or a single path segment. Use encodeURI only when you have a full URL and want to leave the structural characters (such as / ? & = #) intact.
Why does my space turn into a + sign sometimes and %20 other times?
Inside the query string of an application/x-www-form-urlencoded form, a space is encoded as +. Everywhere else in a URL a space should be encoded as %20. Decoders must know the context to turn + back into a space correctly.