Understanding URL Encoding: Why %20 Means Space
If you've ever seen %20 in a URL and wondered what's going on, you've encountered URL encoding (also called percent-encoding). It's one of those fundamental web technologies that developers use daily but rarely think about. This guide explains what URL encoding is, why it exists, and how to use it correctly.
Why URLs Need Encoding
URLs can only contain a limited set of characters from the ASCII character set. The RFC 3986 standard defines these as "unreserved" characters that can appear in a URL without encoding:
A-Z a-z 0-9 - _ . ~ Everything else — spaces, unicode characters, and special characters like &, =, ?, # — must be encoded. This is because these characters have special meaning in URL syntax:
| Character | URL Role | Encoded As |
|---|---|---|
? | Starts query string | %3F |
& | Separates query parameters | %26 |
= | Separates key from value | %3D |
# | Starts fragment identifier | %23 |
/ | Path separator | %2F |
space | Not allowed | %20 or + |
Without encoding, a search query like cats & dogs in a URL parameter would break the URL parser — the & would be interpreted as a parameter separator instead of the literal text.
How Percent-Encoding Works
The encoding algorithm is simple:
- Take the character's byte value in UTF-8
- Convert each byte to a two-digit hexadecimal number
- Prefix each with
%
Space → byte 0x20 → %20
! → byte 0x21 → %21
@ → byte 0x40 → %40
# Multi-byte UTF-8 characters get multiple %XX sequences:
é → bytes 0xC3 0xA9 → %C3%A9
日 → bytes 0xE6 0x97 0xA5 → %E6%97%A5
🎉 → bytes 0xF0 0x9F 0x8E 0x89 → %F0%9F%8E%89 The Space Problem: %20 vs +
Spaces are the most commonly encoded character, and confusingly, there are two encodings:
%20— the standard percent-encoding (RFC 3986). Used in path segments and general URLs.+— used specifically inapplication/x-www-form-urlencodedformat (HTML form submissions).
# In a URL path:
https://example.com/my%20file.pdf
# In a query string (form-encoded):
https://example.com/search?q=hello+world
# Both are valid here:
https://example.com/search?q=hello%20world The + for spaces convention comes from the early web and HTML form encoding. Modern APIs typically use %20 everywhere for consistency.
URL Encoding in JavaScript
JavaScript provides four built-in functions, and choosing the right one matters:
// encodeURIComponent — for encoding individual parameter VALUES
const query = encodeURIComponent("cats & dogs");
// "cats%20%26%20dogs"
const url = `https://example.com/search?q=${query}`;
// decodeURIComponent — reverse of above
decodeURIComponent("cats%20%26%20dogs");
// "cats & dogs"
// encodeURI — for encoding a FULL URL (preserves :, /, ?, &, =, #)
encodeURI("https://example.com/path with spaces?q=hello world");
// "https://example.com/path%20with%20spaces?q=hello%20world"
// URLSearchParams — best for building query strings
const params = new URLSearchParams({
q: "cats & dogs",
page: "1",
filter: "name=fluffy"
});
params.toString();
// "q=cats+%26+dogs&page=1&filter=name%3Dfluffy" Which function to use?
| Scenario | Function |
|---|---|
| Encoding a query parameter value | encodeURIComponent() |
| Encoding a complete URL with special chars | encodeURI() |
| Building a query string from key-value pairs | new URLSearchParams() |
| Encoding a path segment | encodeURIComponent() |
URL Encoding in Python
from urllib.parse import quote, unquote, urlencode, parse_qs
# Encode a path segment or parameter value
quote("cats & dogs")
# "cats%20%26%20dogs"
# Encode with safe characters (don't encode /)
quote("path/to/cats & dogs", safe="/")
# "path/to/cats%20%26%20dogs"
# Decode
unquote("cats%20%26%20dogs")
# "cats & dogs"
# Build a query string from a dictionary
urlencode({"q": "cats & dogs", "page": "1"})
# "q=cats+%26+dogs&page=1"
# Parse a query string back to a dictionary
parse_qs("q=cats+%26+dogs&page=1")
# {"q": ["cats & dogs"], "page": ["1"]} Common Mistakes
1. Double-encoding
The most frequent bug — encoding a string that's already encoded:
// WRONG — double-encodes the %
const encoded = encodeURIComponent("hello%20world");
// "hello%2520world" — %25 is the encoding of %
// RIGHT — encode the original string
const encoded = encodeURIComponent("hello world");
// "hello%20world" 2. Using encodeURI for query parameters
// WRONG — encodeURI doesn't encode & and =
const url = "https://api.com?q=" + encodeURI("key=value&more");
// "https://api.com?q=key=value&more" — broken!
// RIGHT — encodeURIComponent encodes everything
const url = "https://api.com?q=" + encodeURIComponent("key=value&more");
// "https://api.com?q=key%3Dvalue%26more" — correct! 3. Forgetting to encode user input
Any user-provided data in URLs must be encoded. Unencoded input can break URLs and enable injection attacks:
// DANGEROUS — user input directly in URL
const url = `https://api.com/users/${username}`;
// If username is "../admin", this traverses paths
// SAFE — encode the user input
const url = `https://api.com/users/${encodeURIComponent(username)}`; URL Encoding vs Other Encodings
URL encoding is one of several text-encoding schemes used in web development:
| Encoding | Purpose | Example |
|---|---|---|
| URL encoding | Safe characters in URLs | hello%20world |
| Base64 | Binary data as ASCII text | aGVsbG8gd29ybGQ= |
| HTML entities | Special characters in HTML | & < > |
| Unicode escapes | Unicode in source code | \u0048\u0065\u006C\u006C\u006F |
Each encoding solves a different problem — don't mix them up. URL encoding is only for URLs, Base64 is for binary-to-text conversion, and HTML entities are for HTML documents.
Quick Reference
| Language | Encode Component | Decode |
|---|---|---|
| JavaScript | encodeURIComponent(str) | decodeURIComponent(str) |
| Python | urllib.parse.quote(str) | urllib.parse.unquote(str) |
| Go | url.QueryEscape(str) | url.QueryUnescape(str) |
| Java | URLEncoder.encode(str, "UTF-8") | URLDecoder.decode(str, "UTF-8") |
| PHP | urlencode($str) | urldecode($str) |
| CLI (curl) | curl --data-urlencode "q=hello world" | — |
Need to quickly encode or decode a URL string? Our URL Encoder/Decoder handles it instantly in your browser. For converting curl commands with encoded URLs into JavaScript, try the Curl Converter.