All Tools / Blog / URL Encoding Special Characters: A Practical Guide

URL Encoding Special Characters: A Practical Guide

3 min read

URL encoding (also called percent-encoding) converts characters that aren't allowed in URLs into a safe format. A space becomes %20, a & becomes %26, and so on. Understanding when and how to apply it saves you from broken links, failed API calls, and hard-to-debug 400 errors.

Why URL encoding exists

URLs can only contain a limited set of characters: letters (A–Z, a–z), digits (0–9), and a handful of special characters (-, _, ., ~). Everything else — spaces, non-ASCII characters, characters that have structural meaning in URLs like ?, &, #, = — must be percent-encoded when used as data values rather than URL structure.

If someone's name is John & Jane and you put it in a query string unencoded:

/search?name=John & Jane

The browser interprets & as a parameter separator and sends name=John and Jane as two separate things. Encoded correctly:

/search?name=John%20%26%20Jane

Now the server receives name=John & Jane as intended.

JavaScript: encodeURIComponent vs encodeURI

JavaScript has two encoding functions and the difference matters.

encodeURIComponent — encodes everything except letters, digits, and -_.!~*'(). Use this for values inside a URL (query parameters, path segments).

const name = "John & Jane";
const url = `/search?name=${encodeURIComponent(name)}`;
// /search?name=John%20%26%20Jane

encodeURI — encodes everything except characters that are valid in a complete URL structure (including ?, &, =, /, #). Use this for complete URLs.

const url = "https://example.com/search?name=John & Jane";
const encoded = encodeURI(url);
// https://example.com/search?name=John%20&%20Jane  ← & NOT encoded!

Notice encodeURI didn't encode & because & is a valid URL character. For query parameter values, always use encodeURIComponent.

For building URLs with multiple parameters, use URLSearchParams — it handles encoding automatically:

const params = new URLSearchParams({
    name: "John & Jane",
    category: "food & drink",
    page: 1,
});
const url = `/search?${params.toString()}`;
// /search?name=John+%26+Jane&category=food+%26+drink&page=1

Note: URLSearchParams uses + for spaces instead of %20 (form encoding). Both are valid in query strings.

Python: urllib.parse

from urllib.parse import quote, quote_plus, urlencode

# Encode a single value (path segment or query value)
value = "John & Jane"
encoded = quote(value)
print(encoded)
# John%20%26%20Jane

# urlencode for building query strings from a dict
params = {"name": "John & Jane", "category": "food & drink"}
query_string = urlencode(params)
print(query_string)
# name=John+%26+Jane&category=food+%26+drink

# quote_plus uses + for spaces (form encoding, matches urlencode)
encoded_plus = quote_plus(value)
print(encoded_plus)
# John+%26+Jane

For decoding:

from urllib.parse import unquote, unquote_plus

decoded = unquote("John%20%26%20Jane")
print(decoded)
# John & Jane

decoded_plus = unquote_plus("John+%26+Jane")
print(decoded_plus)
# John & Jane

Common encoded characters reference

Character Encoded Notes
Space %20 or + + only valid in query strings
& %26 Parameter separator
= %3D Key-value separator
+ %2B Literal plus (not a space)
/ %2F Path separator
? %3F Query string start
# %23 Fragment identifier
@ %40 User info in URLs
% %25 Percent itself
: %3A Protocol separator

Non-ASCII characters

Characters outside ASCII (accented letters, emoji, CJK characters) are first encoded to UTF-8 bytes, then each byte is percent-encoded.

é → UTF-8 bytes C3 A9%C3%A9

encodeURIComponent("café")
// "caf%C3%A9"
from urllib.parse import quote
quote("café")
# 'caf%C3%A9'

Key takeaways

  • Use encodeURIComponent in JavaScript and quote() in Python for query parameter values and path segments.
  • Use URLSearchParams (JS) or urlencode() (Python) to build full query strings from dictionaries.
  • + and %20 are both valid for spaces in query strings; %20 is safer in path segments.
  • Encode when building URLs, decode when reading URL parameters — never double-encode.