URL Encoding: Making URLs Safe for the Web
You've copied a URL and seen %20 where a space should be, or %C3%A9 where an accented character was. That's URL encoding (also called percent-encoding), and it exists because URLs can only contain a specific set of ASCII characters.
Why URLs Need Encoding
URLs have reserved characters with special meanings:
| Character | Meaning in URL |
|---|---|
/ | Path separator |
? | Start of query string |
& | Query parameter separator |
= | Key-value separator |
# | Fragment identifier |
: | Scheme separator (http:) |
@ | User info separator |
If your data contains these characters, you must encode them to avoid ambiguity. Otherwise the browser can't tell if & is part of your data or a query separator.
How Encoding Works
Each unsafe character is replaced with % followed by its two-digit hex value:
| Character | Encoded | Hex Value |
|---|---|---|
| Space | %20 | 0x20 |
! | %21 | 0x21 |
# | %23 | 0x23 |
& | %26 | 0x26 |
+ | %2B | 0x2B |
/ | %2F | 0x2F |
é | %C3%A9 | UTF-8: 0xC3 0xA9 |
Non-ASCII characters (accents, CJK, emoji) are first converted to UTF-8 bytes, then each byte is percent-encoded.
JavaScript: encodeURI vs encodeURIComponent
This is the single most confusing API in JavaScript:
// encodeURI — encodes a FULL URL
// Does NOT encode: : / ? # [ ] @ ! $ & ' ( ) * + , ; =
encodeURI('https://example.com/path with spaces?q=hello world')
// "https://example.com/path%20with%20spaces?q=hello%20world"
// encodeURIComponent — encodes a COMPONENT (value)
// DOES encode: : / ? # @ ! $ & ' ( ) * + , ; =
encodeURIComponent('price=10¤cy=USD')
// "price%3D10%26currency%3DUSD"Rule of thumb:
- Use
encodeURIComponent()for query parameter values - Use
encodeURI()only when encoding a complete URL that already has the correct structure - Better yet: use the
URLandURLSearchParamsAPIs — they handle encoding automatically
The Modern Way: URL and URLSearchParams
// Build URLs safely — no manual encoding needed
const url = new URL('https://api.example.com/search');
url.searchParams.set('q', 'café & croissant');
url.searchParams.set('page', '1');
console.log(url.toString());
// "https://api.example.com/search?q=caf%C3%A9+%26+croissant&page=1"
// Parse existing URLs
const parsed = new URL('https://example.com/path?name=Jo%C3%A3o');
console.log(parsed.searchParams.get('name'));
// "João" — automatically decodedCommon Encoding Mistakes
Double encoding: Encoding an already-encoded string. %20 becomes %2520. This happens when you call encodeURIComponent on a value that was already encoded.
Not encoding at all: Passing raw user input into URLs. If someone's name is "O'Brien & Sons", your URL breaks.
Using + for spaces: In query strings, + means space (from HTML form encoding). But in path segments, + is a literal plus sign. %20 always means space everywhere.
# Python
from urllib.parse import quote, unquote, urlencode
quote('café & croissant') # 'caf%C3%A9%20%26%20croissant'
unquote('caf%C3%A9') # 'café'
# Build query strings
urlencode({'q': 'hello world', 'lang': 'pt'})
# 'q=hello+world&lang=pt'// Go
import "net/url"
encoded := url.QueryEscape("café & croissant")
// "caf%C3%A9+%26+croissant"
decoded, _ := url.QueryUnescape(encoded)
// "café & croissant"When to Encode What
| Part of URL | Encode With | Example |
|---|---|---|
| Full URL | encodeURI() / new URL() | https://example.com/my%20page |
| Query param value | encodeURIComponent() / URLSearchParams | ?q=caf%C3%A9 |
| Path segment | encodeURIComponent() | /users/Jo%C3%A3o |
| Form data | application/x-www-form-urlencoded | name=Jo%C3%A3o&city=S%C3%A3o+Paulo |
Encode/decode now: URL Encoder/Decoder — paste any text or URL, encode or decode instantly.