Type a space into a URL bar and your browser silently replaces it with %20. That's URL encoding — and understanding it is essential for anyone building web apps, working with APIs, or debugging network requests.
Why URLs need encoding
URLs are restricted to a small set of safe characters: letters (A–Z, a–z), digits (0–9), and a handful of punctuation marks (-, _, ., ~). Everything else — spaces, ampersands, question marks, Unicode characters, slashes that aren't path separators — needs to be encoded before it can appear safely in a URL.
Without encoding, characters like & would be misread as query parameter separators, ? would signal the start of a query string, and spaces would make the URL invalid entirely.
What is percent-encoding?
Percent-encoding (also called URL encoding) represents a character as %XX, where XX is the character's hexadecimal ASCII (or UTF-8) byte value.
| Character | Encoded |
|---|---|
| space | %20 |
& |
%26 |
= |
%3D |
# |
%23 |
+ |
%2B |
/ |
%2F |
? |
%3F |
@ |
%40 |
Note on spaces: You'll see spaces encoded as either %20 or +. The + convention comes from HTML form encoding (application/x-www-form-urlencoded). In a query string, + and %20 both mean space. In a path segment, only %20 is correct — + is a literal plus sign.
encodeURI vs encodeURIComponent
JavaScript provides two encoding functions, and mixing them up is a common source of bugs.
encodeURI(url) encodes a complete URL. It does not encode characters that have structural meaning in URLs: :, /, ?, #, &, =, @. Use it when you already have a full URL and just want to make it safe.
encodeURI("https://example.com/search?q=hello world")
// "https://example.com/search?q=hello%20world"
encodeURIComponent(value) encodes a single component (a query parameter value, a path segment). It encodes everything that encodeURI leaves alone. Use it for individual values you're inserting into a URL.
encodeURIComponent("price=100¤cy=€")
// "price%3D100%26currency%3D%E2%82%AC"
The rule: if you're building a URL from parts, use encodeURIComponent on each dynamic value, not on the whole URL.
URL encoding in code
JavaScript:
// Encode a query parameter value
const query = encodeURIComponent("café & more");
const url = `https://example.com/search?q=${query}`;
// Decode
const original = decodeURIComponent("caf%C3%A9%20%26%20more");
console.log(original); // "café & more"
Python:
from urllib.parse import quote, unquote, urlencode
# Encode a single value
encoded = quote("café & more")
print(encoded) # caf%C3%A9%20%26%20more
# Encode query parameters (dict → query string)
params = {"q": "hello world", "lang": "en"}
qs = urlencode(params)
print(qs) # q=hello+world&lang=en
# Decode
print(unquote("caf%C3%A9%20%26%20more")) # café & more
Go:
import (
"fmt"
"net/url"
)
// Encode
encoded := url.QueryEscape("café & more")
fmt.Println(encoded) // caf%C3%A9+%26+more
// Build a URL with query params
params := url.Values{}
params.Set("q", "hello world")
params.Set("lang", "en")
fmt.Println(params.Encode()) // lang=en&q=hello+world
// Decode
decoded, _ := url.QueryUnescape("caf%C3%A9%20%26%20more")
fmt.Println(decoded) // café & more
PHP:
// Encode
$encoded = urlencode("café & more");
echo $encoded; // caf%C3%A9+%26+more
// Encode for path segments (spaces → %20, not +)
$encoded = rawurlencode("café & more");
echo $encoded; // caf%C3%A9%20%26%20more
// Decode
echo urldecode("caf%C3%A9+%26+more"); // café & more
Command line (curl):
curl -G "https://example.com/search" \
--data-urlencode "q=café & more"
Unicode and multi-byte characters
Characters outside ASCII — like é (U+00E9) or € (U+20AC) — are first encoded as UTF-8, then each byte is percent-encoded.
é in UTF-8 is bytes 0xC3 0xA9, so it becomes %C3%A9.
€ in UTF-8 is bytes 0xE2 0x82 0xAC, so it becomes %E2%82%AC.
This means a single character can expand into 9 characters in the URL. Long Unicode strings can produce very long encoded URLs — something to keep in mind for URL length limits (typically 2,000–8,000 characters depending on the browser and server).
Common mistakes
Double-encoding. If you encode %20 again, you get %2520 (the % itself becomes %25). This happens when a URL is encoded twice — once by your code, once by a library. The symptom is %25 appearing unexpectedly in your URLs or decoded values.
Encoding the entire URL. encodeURIComponent("https://example.com/path") will encode the :// and destroy the URL structure. Always encode only the dynamic parts, not the full URL.
Not encoding form data. When constructing application/x-www-form-urlencoded bodies manually, remember to encode both keys and values, and join them with &, not raw spaces.
Encode a URL string online
If you need to quickly encode or decode a URL string without writing code, use the URL Encoder/Decoder tool. Paste in a string, get the encoded or decoded result instantly. Runs entirely in your browser — nothing is sent to a server.
FAQ
Q: What's the difference between URL encoding and Base64 encoding?
URL encoding (%XX) is designed to make any string safe to appear in a URL. Base64 is a way to represent binary data using only printable ASCII characters — it's not URL-safe by default (it uses + and /). Use URL encoding for query parameters; use Base64 when you need to embed binary data (images, files) in text contexts.
Q: Should I encode slashes in path segments?
It depends. A / in a path segment that's a literal slash (not a path separator) must be encoded as %2F. However, some servers treat %2F in paths the same as / and route them identically. If you're passing a value that contains slashes as a path parameter, prefer putting it in the query string instead.
Q: Why does my browser show the decoded URL in the address bar?
Modern browsers decode URLs for display, since https://example.com/search?q=café is more readable than https://example.com/search?q=caf%C3%A9. The actual HTTP request still uses the encoded form.
Q: What characters are safe and don't need encoding?
Per RFC 3986, the "unreserved" characters never need encoding: A-Z a-z 0-9 - _ . ~. All other characters should be encoded in most contexts.