URL Encoding Special Characters: A Practical Guide
URL encoding (also called percent-encoding) converts characters that aren't allowed in URLs into a safe format. A space becomes %20, a & becomes %26, and so on. Understanding when and how to apply it saves you from broken links, failed API calls, and hard-to-debug 400 errors.
Why URL encoding exists
URLs can only contain a limited set of characters: letters (A–Z, a–z), digits (0–9), and a handful of special characters (-, _, ., ~). Everything else — spaces, non-ASCII characters, characters that have structural meaning in URLs like ?, &, #, = — must be percent-encoded when used as data values rather than URL structure.
If someone's name is John & Jane and you put it in a query string unencoded:
/search?name=John & Jane
The browser interprets & as a parameter separator and sends name=John and Jane as two separate things. Encoded correctly:
/search?name=John%20%26%20Jane
Now the server receives name=John & Jane as intended.
JavaScript: encodeURIComponent vs encodeURI
JavaScript has two encoding functions and the difference matters.
encodeURIComponent — encodes everything except letters, digits, and -_.!~*'(). Use this for values inside a URL (query parameters, path segments).
const name = "John & Jane";
const url = `/search?name=${encodeURIComponent(name)}`;
// /search?name=John%20%26%20Jane
encodeURI — encodes everything except characters that are valid in a complete URL structure (including ?, &, =, /, #). Use this for complete URLs.
const url = "https://example.com/search?name=John & Jane";
const encoded = encodeURI(url);
// https://example.com/search?name=John%20&%20Jane ← & NOT encoded!
Notice encodeURI didn't encode & because & is a valid URL character. For query parameter values, always use encodeURIComponent.
For building URLs with multiple parameters, use URLSearchParams — it handles encoding automatically:
const params = new URLSearchParams({
name: "John & Jane",
category: "food & drink",
page: 1,
});
const url = `/search?${params.toString()}`;
// /search?name=John+%26+Jane&category=food+%26+drink&page=1
Note: URLSearchParams uses + for spaces instead of %20 (form encoding). Both are valid in query strings.
Python: urllib.parse
from urllib.parse import quote, quote_plus, urlencode
# Encode a single value (path segment or query value)
value = "John & Jane"
encoded = quote(value)
print(encoded)
# John%20%26%20Jane
# urlencode for building query strings from a dict
params = {"name": "John & Jane", "category": "food & drink"}
query_string = urlencode(params)
print(query_string)
# name=John+%26+Jane&category=food+%26+drink
# quote_plus uses + for spaces (form encoding, matches urlencode)
encoded_plus = quote_plus(value)
print(encoded_plus)
# John+%26+Jane
For decoding:
from urllib.parse import unquote, unquote_plus
decoded = unquote("John%20%26%20Jane")
print(decoded)
# John & Jane
decoded_plus = unquote_plus("John+%26+Jane")
print(decoded_plus)
# John & Jane
Common encoded characters reference
| Character | Encoded | Notes |
|---|---|---|
| Space | %20 or + |
+ only valid in query strings |
& |
%26 |
Parameter separator |
= |
%3D |
Key-value separator |
+ |
%2B |
Literal plus (not a space) |
/ |
%2F |
Path separator |
? |
%3F |
Query string start |
# |
%23 |
Fragment identifier |
@ |
%40 |
User info in URLs |
% |
%25 |
Percent itself |
: |
%3A |
Protocol separator |
Non-ASCII characters
Characters outside ASCII (accented letters, emoji, CJK characters) are first encoded to UTF-8 bytes, then each byte is percent-encoded.
é → UTF-8 bytes C3 A9 → %C3%A9
encodeURIComponent("café")
// "caf%C3%A9"
from urllib.parse import quote
quote("café")
# 'caf%C3%A9'
Key takeaways
- Use
encodeURIComponentin JavaScript andquote()in Python for query parameter values and path segments. - Use
URLSearchParams(JS) orurlencode()(Python) to build full query strings from dictionaries. +and%20are both valid for spaces in query strings;%20is safer in path segments.- Encode when building URLs, decode when reading URL parameters — never double-encode.