URL Encoding Explained: Percent Encoding for Web Developers
A complete guide to URL encoding covering why it's necessary, how percent encoding works, and the difference between encodeURI and encodeURIComponent.
Why URL Encoding is Needed
URLs can only contain a limited set of characters from the ASCII character set. When you need to include special characters, spaces, or non-ASCII characters (like Unicode) in a URL, they must be encoded.
Without Encoding
❌ https://toolsdock.com/search?q=hello world&lang=日本語
Problem: space breaks URL, non-ASCII characters may fail
With Encoding
✅ https://toolsdock.com/search?q=hello%20world&lang=%E6%97%A5%E6%9C%AC%E8%AA%9E
All characters are safe ASCII
What Happens Without Encoding?
- Broken URLs: Browsers may not navigate correctly
- Data loss: Special characters may be misinterpreted
- Security issues: Unencoded characters can enable injection attacks
- Server errors: Web servers may reject malformed URLs
How Percent Encoding Works
Percent encoding (also called URL encoding) replaces unsafe characters with a percent sign (%) followed by two hexadecimal digits representing the character's byte value.
Encoding Process
- Convert the character to its UTF-8 byte sequence
- Convert each byte to two hexadecimal digits
- Prefix each pair with a percent sign
Examples
| Character | UTF-8 Bytes | Percent Encoded |
|---|---|---|
| Space | 0x20 | %20 |
| & | 0x26 | %26 |
| = | 0x3D | %3D |
| ? | 0x3F | %3F |
| é | 0xC3 0xA9 | %C3%A9 |
| 中 | 0xE4 0xB8 0xAD | %E4%B8%AD |
Special Case: Space
Spaces can be encoded as %20 or +, but they're not interchangeable:
%20is the standard percent encoding+is only valid in query string values (application/x-www-form-urlencoded)
// In URL path: only %20 is correct
https://toolsdock.com/my%20file.pdf ✅
https://toolsdock.com/my+file.pdf ❌ (+ is a literal plus)
// In query string: both work
?name=John%20Doe ✅
?name=John+Doe ✅
Safe vs Reserved Characters
Unreserved (Safe) Characters
These never need encoding:
A-Z a-z 0-9 - _ . ~
Reserved Characters
These have special meaning in URLs and must be encoded if used literally:
| Character | Purpose in URL | Encoded |
|---|---|---|
: | Scheme separator (https:) | %3A |
/ | Path separator | %2F |
? | Query string start | %3F |
# | Fragment identifier | %23 |
& | Query parameter separator | %26 |
= | Key-value separator | %3D |
@ | User info separator | %40 |
Context Matters
// & in query string - don't encode (it's a separator)
?name=John&age=30
// & in a value - must encode
?company=Johnson%26Johnson
// / in path - don't encode (it's a separator)
/users/123/profile
// / in a value - must encode
?path=%2Fusers%2F123
encodeURI vs encodeURIComponent
JavaScript provides two encoding functions with different purposes:
encodeURI()
Use for encoding a complete URL. Preserves URL structure characters.
const url = "https://toolsdock.com/path with spaces?q=hello world";
encodeURI(url);
// "https://toolsdock.com/path%20with%20spaces?q=hello%20world"
// Preserved: : / ? & = #
Does NOT encode: A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) #
encodeURIComponent()
Use for encoding a URL component (query parameter value, path segment).
const value = "hello?world&foo=bar";
encodeURIComponent(value);
// "hello%3Fworld%26foo%3Dbar"
// All special characters are encoded
Does NOT encode: A-Z a-z 0-9 - _ . ! ~ * ' ( )
Comparison
| Character | encodeURI | encodeURIComponent |
|---|---|---|
| Space | %20 | %20 |
| ? | ? (unchanged) | %3F |
| & | & (unchanged) | %26 |
| = | = (unchanged) | %3D |
| / | / (unchanged) | %2F |
| # | # (unchanged) | %23 |
When to Use Each
// Building a URL with user input in query params
const search = "cats & dogs";
const url = "https://toolsdock.com/search?q=" + encodeURIComponent(search);
// https://toolsdock.com/search?q=cats%20%26%20dogs ✅
// Wrong: encodeURI won't encode the &
const badUrl = "https://toolsdock.com/search?q=" + encodeURI(search);
// https://toolsdock.com/search?q=cats%20&%20dogs ❌
// The & breaks the query string!
Common Scenarios
1. Building Query Strings
// Safe way to build query strings
function buildQueryString(params) {
return Object.entries(params)
.map(([key, value]) =>
encodeURIComponent(key) + '=' + encodeURIComponent(value)
)
.join('&');
}
const params = {
search: "hello world",
filter: "price<100",
category: "food&drinks"
};
buildQueryString(params);
// "search=hello%20world&filter=price%3C100&category=food%26drinks"
// Or use URLSearchParams (modern browsers)
const searchParams = new URLSearchParams(params);
searchParams.toString();
// Same result, handles encoding automatically
2. Embedding URLs in URLs
// Redirect URL that contains another URL
const returnUrl = "https://mysite.com/callback?token=abc123";
const loginUrl = "https://toolsdock.com/login?redirect="
+ encodeURIComponent(returnUrl);
// Result:
// https://auth.toolsdock.com/login?redirect=https%3A%2F%2Fmysite.com%2Fcallback%3Ftoken%3Dabc123
3. Path Segments with Special Characters
// File name with spaces
const filename = "My Document (Final).pdf";
const url = "/files/" + encodeURIComponent(filename);
// /files/My%20Document%20(Final).pdf
4. Form Data
// Standard form encoding (application/x-www-form-urlencoded)
// Spaces become + instead of %20
const formData = "name=John+Doe&message=Hello+World";
// When manually encoding for forms:
function formEncode(str) {
return encodeURIComponent(str).replace(/%20/g, '+');
}
5. Non-ASCII Characters
// Unicode characters are UTF-8 encoded then percent-encoded
encodeURIComponent("日本語");
// "%E6%97%A5%E6%9C%AC%E8%AA%9E"
encodeURIComponent("é");
// "%C3%A9"
encodeURIComponent("🎉");
// "%F0%9F%8E%89"
Debugging URL Issues
Common Problems
1. Double Encoding
// Already encoded input gets encoded again
const input = "hello%20world"; // Already encoded
encodeURIComponent(input);
// "hello%2520world" - %20 became %2520!
// Fix: Only encode raw strings, or decode first
decodeURIComponent(input); // "hello world"
encodeURIComponent(decodeURIComponent(input)); // "hello%20world"
2. Wrong Function Choice
// Using encodeURI for a parameter value
const search = "price < 100 & discount";
"?q=" + encodeURI(search);
// "?q=price%20%3C%20100%20&%20discount" - & breaks the URL!
// Fix: Use encodeURIComponent for values
"?q=" + encodeURIComponent(search);
// "?q=price%20%3C%20100%20%26%20discount" ✅
3. Encoding the Entire URL
// Wrong: Encoding an already-valid URL
const url = "https://toolsdock.com/path?q=test";
encodeURIComponent(url);
// "https%3A%2F%2Ftoolsdock.com%2Fpath%3Fq%3Dtest" - Broken!
// If you need to pass a URL as a parameter:
const base = "https://redirect.com/?url=";
base + encodeURIComponent(url); // Correct
Debugging Tips
- Use browser dev tools Network tab to inspect actual URLs sent
- Decode suspicious URLs with our URL Decoder
- Check for %25 - it means % was encoded, indicating double encoding
- Test with characters like
& = ? #to verify proper encoding
Tools and Resources
Quick Reference
Use encodeURIComponent
- Query parameter values
- Path segments with special chars
- URL embedded in another URL
- Any user input in URLs
Use encodeURI
- Complete URL with spaces only
- Rarely needed in practice
- When URL structure must be preserved