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)
⚠️ Common mistake: Using 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.

💡 Quick rule: Use 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

  1. Double encoding: Encoding an already-encoded string turns %20 into %2520. Always encode raw values, not already-encoded strings.
  2. Forgetting to encode: URLs with unencoded spaces or special characters may work in some browsers but fail in others or in API calls.
  3. Wrong function: Using encodeURI for parameter values or encodeURIComponent for full URLs.
  4. 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