URL Encoding & Decoding Guide: Percent-Encoding Explained
Every time you see %20 in a URL or wonder why your query parameters aren't working, you're dealing with URL encoding. Also known as "percent-encoding," it's the mechanism that ensures special characters travel safely through URLs. This guide explains the rules, the common functions, and the mistakes developers make most often.
Why URL Encoding Exists
URLs (Uniform Resource Locators) can only contain a limited set of ASCII characters. Characters like spaces, &, =, ?, and # have structural meaning in URLs, so they can't appear literally in data values. URL encoding converts these characters into a safe format using percent signs followed by two hex digits.
URL Structure
https://example.com:443/path/to/page?key=value&name=hello#section
|___| |__________|___|___________|________________________|______|
scheme host port path query string fragment
Characters like ?, &, =, and # are delimiters. If your data happens to contain these characters, they must be percent-encoded to avoid ambiguity.
How Percent-Encoding Works
Each byte is encoded as %HH, where HH is the two-digit hexadecimal representation of the byte value:
Space → %20
! → %21
# → %23
$ → %24
& → %26
+ → %2B
/ → %2F
= → %3D
? → %3F
@ → %40
For non-ASCII characters (like Unicode), the character is first encoded to UTF-8 bytes, then each byte is percent-encoded:
// "café" → UTF-8 bytes for "é" are 0xC3 0xA9
"café" → "caf%C3%A9"
// "日本" → UTF-8 bytes: E6 97 A5 E6 9C AC
"日本" → "%E6%97%A5%E6%9C%AC"
JavaScript Encoding Functions
JavaScript provides three pairs of encoding/decoding functions, each with different behavior:
encodeURIComponent / decodeURIComponent
Encodes everything except: A-Z a-z 0-9 - _ . ! ~ * ' ( )
Use this for encoding individual query parameter values:
encodeURIComponent("hello world") // "hello%20world"
encodeURIComponent("price=10&qty=2") // "price%3D10%26qty%3D2"
encodeURIComponent("café") // "caf%C3%A9"
// Building a query string
const query = `?search=${encodeURIComponent("a&b=c")}&page=1`;
// "?search=a%26b%3Dc&page=1"
encodeURI / decodeURI
Encodes special characters but preserves URL structural characters (: / ? # [ ] @ ! $ & ' ( ) * + , ; =):
encodeURI("https://example.com/path?q=hello world")
// "https://example.com/path?q=hello%20world"
// Does NOT encode & or = (they're URL delimiters)
encodeURI("key=value&name=test")
// "key=value&name=test" (unchanged)
encodeURI instead of encodeURIComponent for query values. If your value contains & or =, encodeURI won't encode them, breaking your query string.
escape / unescape (Deprecated)
These legacy functions use a non-standard encoding and should not be used. They don't handle UTF-8 properly and are excluded from modern standards.
encodeURIComponent() for query parameter values. Use encodeURI() only when encoding a complete URL.
URL Encoding in Python
from urllib.parse import quote, unquote, urlencode
# Encode a single value
quote("hello world") # "hello%20world"
quote("hello world", safe="") # "hello%20world" (encode everything)
quote("path/to/file") # "path/to/file" (/ is safe by default)
quote("path/to/file", safe="") # "path%2Fto%2Ffile"
# Decode
unquote("hello%20world") # "hello world"
# Build query strings
params = {"q": "hello world", "page": "1", "filter": "a&b"}
urlencode(params)
# "q=hello+world&page=1&filter=a%26b"
URL Encoding on the Command Line
# Python one-liner
python3 -c "from urllib.parse import quote; print(quote('hello world'))"
# hello%20world
# Using curl with encoded params
curl "https://api.example.com/search?q=$(python3 -c "from urllib.parse import quote; print(quote('hello world'))")"
# Decode
python3 -c "from urllib.parse import unquote; print(unquote('hello%20world'))"
Space Encoding: %20 vs +
There are two ways to encode spaces, and the context determines which is correct:
%20— Standard percent-encoding (RFC 3986). Used in URL paths and generic encoding+— Form encoding (application/x-www-form-urlencoded). Used in HTML form submissions and query strings
// JavaScript
encodeURIComponent("a b") // "a%20b" (uses %20)
new URLSearchParams({q: "a b"}).toString() // "q=a+b" (uses +)
// Both are valid in query strings
// https://example.com/search?q=hello%20world ✅
// https://example.com/search?q=hello+world ✅
Common Pitfalls
- Double encoding: Encoding an already-encoded string turns
%20into%2520. Always encode raw values, not already-encoded strings. - Forgetting to encode: URLs with unencoded spaces or special characters may work in some browsers but fail in others or in API calls.
- Wrong function: Using
encodeURIfor parameter values orencodeURIComponentfor full URLs. - Path vs query encoding: Forward slashes
/should NOT be encoded in paths but SHOULD be encoded in query values.
🛠️ Encode & Decode URLs Online
Open URL Tool →Further Reading
- Base64 Encoding Explained — Another encoding scheme for binary data in text contexts
- Regex Tutorial — Match and validate URL patterns with regex
- JSON Formatting Guide — Encoding URLs inside JSON payloads