URL Encoding and Decoding: Essential Guide for Web Developers
URLs can only contain certain characters. Learn how URL encoding works and when to apply it in your web applications.
What is URL Encoding?
URL encoding, also known as percent encoding, converts characters into a format that can be safely transmitted in URLs. Characters that aren't allowed in URLs are replaced with a percent sign (%) followed by their hexadecimal ASCII value.
Why URL Encoding Exists
URLs can only contain a limited set of characters:
- Letters: A-Z, a-z
- Numbers: 0-9
- Special: - _ . ~
- Reserved: : / ? # [ ] @ ! $ & ' ( ) * + , ; =
How It Works
Space → %20
& → %26
= → %3D
? → %3F
# → %23
% → %25
+ → %2BExample:
Original: Hello World!
Encoded: Hello%20World%21Reserved vs. Unreserved Characters
Unreserved (Never need encoding)
A-Z a-z 0-9 - _ . ~Reserved (Encode when used as data)
| Character | Purpose | Encoded |
|---|---|---|
| : | Protocol separator | %3A |
| / | Path separator | %2F |
| ? | Query start | %3F |
| # | Fragment start | %23 |
| [ ] | IPv6 addresses | %5B %5D |
| @ | User info | %40 |
| ! | Sub-delimiter | %21 |
| $ | Sub-delimiter | %24 |
| & | Parameter separator | %26 |
| ' | Sub-delimiter | %27 |
| ( ) | Sub-delimiter | %28 %29 |
| * | Sub-delimiter | %2A |
| + | Space (in query) | %2B |
| , | Sub-delimiter | %2C |
| ; | Parameter separator | %3B |
| = | Key-value separator | %3D |
JavaScript URL Encoding Functions
encodeURIComponent()
Encodes everything except: A-Z a-z 0-9 - _ . ! ~ * ' ( )
// Use for query parameter values
const searchTerm = 'hello world & more';
const encoded = encodeURIComponent(searchTerm);
// Result: "hello%20world%20%26%20more"
const url = `https://example.com/search?q=${encoded}`;
// https://example.com/search?q=hello%20world%20%26%20moreencodeURI()
Encodes everything except: A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) #
// Use for entire URLs (preserves structure)
const url = 'https://example.com/path with spaces/page';
const encoded = encodeURI(url);
// Result: "https://example.com/path%20with%20spaces/page"When to Use Which
| Scenario | Function |
|---|---|
| Query parameter value | encodeURIComponent() |
| Path segment | encodeURIComponent() |
| Entire URL (preserve structure) | encodeURI() |
| Form data | encodeURIComponent() |
Decoding
// Decode component
decodeURIComponent('hello%20world');
// Result: "hello world"
// Decode URI
decodeURI('https://example.com/path%20name');
// Result: "https://example.com/path name"Building URLs Safely
Using URLSearchParams
// The modern way to build query strings
const params = new URLSearchParams();
params.append('name', 'John Doe');
params.append('query', 'hello & world');
params.append('special', '100% complete');
console.log(params.toString());
// "name=John+Doe&query=hello+%26+world&special=100%25+complete"
// Or initialize with object
const params2 = new URLSearchParams({
name: 'John',
age: '30'
});Using URL API
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'hello world');
url.searchParams.set('page', '1');
console.log(url.toString());
// "https://example.com/search?q=hello+world&page=1"
// Parsing existing URLs
const parsed = new URL('https://example.com/path?name=John%20Doe');
console.log(parsed.searchParams.get('name')); // "John Doe"URL Encoding in Different Languages
Python
from urllib.parse import quote, quote_plus, unquote, urlencode
# Encode path component
quote('hello world') # 'hello%20world'
# Encode for query string (space = +)
quote_plus('hello world') # 'hello+world'
# Decode
unquote('hello%20world') # 'hello world'
# Build query string
params = {'name': 'John Doe', 'age': '30'}
urlencode(params) # 'name=John+Doe&age=30'PHP
// Encode
$encoded = urlencode('hello world'); // "hello+world"
$encoded = rawurlencode('hello world'); // "hello%20world"
// Decode
$decoded = urldecode('hello+world'); // "hello world"
$decoded = rawurldecode('hello%20world'); // "hello world"
// Build query string
$params = ['name' => 'John', 'age' => 30];
$query = http_build_query($params); // "name=John&age=30"Java
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
// Encode
String encoded = URLEncoder.encode("hello world", StandardCharsets.UTF_8);
// "hello+world"
// Decode
String decoded = URLDecoder.decode("hello+world", StandardCharsets.UTF_8);
// "hello world"Go
import "net/url"
// Encode
encoded := url.QueryEscape("hello world")
// "hello+world"
// Decode
decoded, _ := url.QueryUnescape("hello+world")
// "hello world"
// Build URL with params
u, _ := url.Parse("https://example.com/search")
q := u.Query()
q.Set("name", "John Doe")
u.RawQuery = q.Encode()
// https://example.com/search?name=John+DoeCommon Encoding Scenarios
1. Search Queries
const searchTerm = 'C++ programming';
const url = `/search?q=${encodeURIComponent(searchTerm)}`;
// /search?q=C%2B%2B%20programming2. File Names in URLs
const fileName = 'Report (Final).pdf';
const url = `/files/${encodeURIComponent(fileName)}`;
// /files/Report%20%28Final%29.pdf3. Redirect URLs
const returnUrl = 'https://example.com/page?id=123';
const loginUrl = `/login?redirect=${encodeURIComponent(returnUrl)}`;
// /login?redirect=https%3A%2F%2Fexample.com%2Fpage%3Fid%3D1234. JSON in Query Strings
const filters = JSON.stringify({ status: 'active', type: 'user' });
const url = `/api/items?filters=${encodeURIComponent(filters)}`;
// /api/items?filters=%7B%22status%22%3A%22active%22%2C%22type%22%3A%22user%22%7DSpace Encoding: %20 vs +
The handling of spaces differs:
| Context | Space becomes |
|---|---|
| Path | %20 |
| Query string (HTML forms) | + |
| encodeURIComponent | %20 |
| URLSearchParams | + |
// Both are valid for query strings
'/search?q=hello%20world' // Works
'/search?q=hello+world' // Also works
// But in paths, only %20 is correct
'/files/my%20file.pdf' // Correct
'/files/my+file.pdf' // Looks for file named "my+file.pdf"Double Encoding Issues
A common mistake is encoding already-encoded strings:
// Original
const url = 'https://example.com/search?q=hello%20world';
// ❌ Double encoding
encodeURI(url);
// "https://example.com/search?q=hello%2520world"
// %25 is the encoded form of %
// ✅ Check if already encoded
function safeEncode(str) {
try {
// If decoding changes nothing, it's not encoded
return decodeURIComponent(str) === str
? encodeURIComponent(str)
: str;
} catch (e) {
// Invalid encoding, encode it
return encodeURIComponent(str);
}
}Handling Unicode
Modern URLs support Unicode through Punycode (for domains) and UTF-8 encoding:
// Unicode in query parameters
const query = '日本語';
encodeURIComponent(query);
// "%E6%97%A5%E6%9C%AC%E8%AA%9E"
// Each UTF-8 byte is percent-encoded
// 日 = E6 97 A5 (3 bytes) = %E6%97%A5Security Considerations
1. Never Trust Encoded Input
// User could submit already-encoded malicious input
const userInput = '%3Cscript%3Ealert(1)%3C/script%3E';
const decoded = decodeURIComponent(userInput);
// "<script>alert(1)</script>"
// Always sanitize after decoding!2. Validate URLs
function isValidUrl(string) {
try {
const url = new URL(string);
return ['http:', 'https:'].includes(url.protocol);
} catch (_) {
return false;
}
}3. Prevent Open Redirects
// ❌ Dangerous - allows redirect to any site
const redirect = req.query.redirect;
res.redirect(redirect);
// ✅ Validate redirect destination
const allowedHosts = ['example.com', 'app.example.com'];
const url = new URL(redirect, 'https://example.com');
if (allowedHosts.includes(url.hostname)) {
res.redirect(redirect);
}Conclusion
URL encoding is fundamental to web development. Understanding when and how to encode URLs prevents bugs, security issues, and data corruption. Use the built-in APIs (URL, URLSearchParams) when possible, and always encode user input before including it in URLs.
Use our free URL Encoder/Decoder tool to quickly encode or decode URLs during development and debugging.
Try Our Free Tools
Put these tips into practice with our free online tools. No signup required.
Explore Tools