Ever wondered why your browser shows %20 instead of spaces, or why your carefully crafted URL broke when you added a search query? Welcome to the world of URL encoding, also known as percent-encoding.
What is URL Encoding?
URL encoding is a mechanism for representing characters that have special meaning in URLs, or characters that aren't allowed in URLs at all. It works by replacing problematic characters with a % followed by two hexadecimal digits representing the character's ASCII value.
For example:
- Space →
%20 - Ampersand (&) →
%26 - Question mark (?) →
%3F
Why URLs Need Encoding
URLs have strict rules about which characters they can contain. The URL specification (RFC 3986) defines two types of characters:
Reserved Characters
These characters have special meanings in URLs:
:separates scheme from path (https:)/separates path segments?begins the query string#begins the fragment&separates query parameters=assigns values to parameters@separates user info from host- And more:
! $ ' ( ) * + , ;
Unreserved Characters
These are always safe and never need encoding:
- Letters:
A-Zanda-z - Digits:
0-9 - Special:
-_.~
Everything else—including spaces, non-ASCII characters, and reserved characters used as data—must be percent-encoded.
The Percent-Encoding Algorithm
Encoding a character is straightforward:
- Determine the character's byte representation (usually UTF-8)
- Convert each byte to its hexadecimal value
- Prefix with
%
For ASCII characters, it's simple:
Space (ASCII 32) → 32 in hex is 20 → %20
& (ASCII 38) → 38 in hex is 26 → %26
For non-ASCII characters (like emoji or accented letters), you get multiple percent-encoded bytes:
é (UTF-8: 0xC3 0xA9) → %C3%A9
🎉 (UTF-8: 0xF0 0x9F 0x8E 0x89) → %F0%9F%8E%89
Characters That Must Be Encoded
Always encode these when they appear in data (not as URL structure):
| Character | Encoded | Reason |
|---|---|---|
| Space | %20 | Not allowed in URLs |
| " | %22 | Can break HTML attributes |
| < | %3C | Security (XSS prevention) |
| > | %3E | Security (XSS prevention) |
| # | %23 | Fragment identifier |
| % | %25 | Encoding escape character |
| %7B %7D | Not allowed | |
| | | %7C | Not allowed |
| \ | %5C | Not allowed |
| ^ | %5E | Not allowed |
| ~ | %7E | Sometimes encoded |
Characters That Should NOT Be Encoded
Don't encode characters that are part of the URL structure:
- The
://after the scheme - The
/separating path segments (unless it's data) - The
?starting query strings - The
#starting fragments - The
&and=in query parameters
Encoding these breaks the URL's meaning.
Common Mistakes
1. Double Encoding
This happens when you encode an already-encoded string:
Original: Hello World
First encoding: Hello%20World
Double encoding: Hello%2520World ← WRONG!
The % got encoded to %25, so %20 became %2520. This is a frequent bug when building URLs programmatically.
2. Encoding Entire URLs
Don't do this:
// WRONG
const url = encodeURIComponent('https://example.com/search?q=hello world');
// Result: https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%20world
This breaks the URL structure. Only encode the values:
// CORRECT
const query = encodeURIComponent('hello world');
const url = `https://example.com/search?q=${query}`;
3. Space: + vs %20
There's historical confusion here:
%20is the standard URL encoding for space+means space only inapplication/x-www-form-urlencoded(HTML forms)
In query strings from HTML forms, you might see either:
?name=John+Doe
?name=John%20Doe
Both are valid, but %20 is more universally understood. Use + only when specifically working with form data.
Query String Encoding
Query strings have their own rules:
https://example.com/search?name=John%20Doe&city=New%20York
- Parameter names and values are encoded
&separates parameters (not encoded)=assigns values (not encoded)
If your value contains & or =, they must be encoded:
const filter = 'status=active&type=user';
const encoded = encodeURIComponent(filter); // status%3Dactive%26type%3Duser
const url = `https://api.example.com/search?filter=${encoded}`;
Path vs Query Encoding Differences
Path segments and query strings have slightly different rules:
Path encoding (more strict):
- Spaces should be
%20 +is a literal plus sign- Some characters like
@and:might need encoding
Query encoding (form-style):
- Spaces can be
+or%20 +is sometimes space (context-dependent)
When in doubt, use %20 for spaces everywhere.
JavaScript: encodeURI vs encodeURIComponent
JavaScript provides two functions with different purposes:
encodeURI()
Encodes a complete URL, preserving structural characters:
encodeURI('https://example.com/path with spaces?query=value')
// "https://example.com/path%20with%20spaces?query=value"
Safe characters: : / ? # [ ] @ ! $ & ' ( ) * + , ; =
encodeURIComponent()
Encodes a URL component (like a query value), encoding most special characters:
encodeURIComponent('hello?world&foo=bar')
// "hello%3Fworld%26foo%3Dbar"
Rule of thumb: Use encodeURIComponent() for individual values, encodeURI() for complete URLs (though building URLs by hand is better).
Modern URL Building
The best practice is using the URL API:
const url = new URL('https://example.com/search');
url.searchParams.set('query', 'hello world');
url.searchParams.set('filter', 'a=1&b=2');
console.log(url.href);
// "https://example.com/search?query=hello+world&filter=a%3D1%26b%3D2"
The URLSearchParams class handles encoding automatically and correctly.
Decoding URLs
To decode, use the inverse functions:
decodeURI('https://example.com/path%20with%20spaces')
// "https://example.com/path with spaces"
decodeURIComponent('hello%3Fworld')
// "hello?world"
Summary
URL encoding ensures special characters don't break your URLs:
- Use
encodeURIComponent()for query parameter values - Use
encodeURI()for complete URLs (sparingly) - Prefer the
URLandURLSearchParamsAPIs for building URLs - Watch out for double encoding
- Remember:
+for spaces is form-specific;%20is universal
Need to encode or decode a URL? Try our URL Encoder/Decoder tool!