You paste a URL into your browser and it works fine. You programmatically construct the same URL in code, add a parameter value with a space or ampersand in it, and suddenly the request breaks or returns unexpected results.
URL encoding — also called percent encoding — is why. Here's what it is, when you need it, and how to apply it correctly.
What Is URL Encoding?
URLs can only contain a limited set of characters defined in RFC 3986: letters (A–Z, a–z), digits (0–9), and a handful of special characters (-, _, ., ~). Everything else must be encoded.
Encoding works by replacing the character with a % followed by its two-digit hexadecimal ASCII code.
| Character | Encoded |
|---|---|
| Space | %20 |
& |
%26 |
= |
%3D |
+ |
%2B |
/ |
%2F |
? |
%3F |
# |
%23 |
@ |
%40 |
So a URL like:
https://example.com/search?q=hello world&sort=date+desc
becomes:
https://example.com/search?q=hello%20world&sort=date%2Bdesc
Reserved vs Unreserved Characters
The tricky part: some characters have special meaning in the URL structure itself and some are just general-purpose.
Reserved characters (!, *, ', (, ), ;, :, @, &, =, +, $, ,, /, ?, #, [, ]) — they can appear in a URL unencoded when used for their structural purpose (e.g., / separates path segments, ? starts the query string). When they appear as data inside a parameter value, they must be encoded.
Unreserved characters (A–Z, a–z, 0–9, -, _, ., ~) — these never need encoding.
This is the source of most bugs: a / in a path segment is fine. A / inside a query parameter value must be %2F.
The + vs %20 Trap
There are two URL encoding specifications in common use:
RFC 3986 (standard URL encoding): encodes space as %20. Use this for full URLs, path segments, and modern API requests.
application/x-www-form-urlencoded (HTML form encoding): encodes space as +. This is what HTML forms and jQuery's $.ajax() use by default.
Mixing them causes bugs. If your backend expects form encoding and you send %20, the %20 gets through fine (most decoders handle both). But if you encode a literal + as + when you mean a plus sign, the decoder interprets it as a space.
The safest rule: use %20 for spaces in all contexts except traditional HTML form submissions. Never rely on + in API parameters.
How to Encode URLs in Code
JavaScript:
// Encode a full URL component (query parameter value)
const query = encodeURIComponent('hello world & more');
// → 'hello%20world%20%26%20more'
// Encode a full URL while preserving structural characters (/, ?, &, =)
const url = encodeURI('https://example.com/search?q=hello world');
// → 'https://example.com/search?q=hello%20world'
Key difference: encodeURIComponent encodes everything including /, ?, & — use for parameter values. encodeURI preserves structural characters — use when encoding a complete URL.
Python:
from urllib.parse import quote, quote_plus, urlencode
# Encode a path segment (preserves /)
quote('/path/to/resource') # → '/path/to/resource'
# Encode a query parameter value
quote('hello world & more') # → 'hello%20world%20%26%20more'
# HTML form encoding (spaces as +)
quote_plus('hello world') # → 'hello+world'
# Build a query string from a dict
urlencode({'q': 'hello world', 'page': 1})
# → 'q=hello+world&page=1' (uses form encoding by default)
# For RFC 3986 encoding in urlencode:
urlencode({'q': 'hello world'}, quote_via=quote)
# → 'q=hello%20world'
PHP:
// Encode a query parameter value (RFC 3986)
rawurlencode('hello world & more');
// → 'hello%20world%20%26%20more'
// Build a query string
http_build_query(['q' => 'hello world', 'page' => 1]);
// → 'q=hello+world&page=1' (uses form encoding)
Decoding URLs
JavaScript:
decodeURIComponent('hello%20world%20%26%20more');
// → 'hello world & more'
decodeURI('https://example.com/search?q=hello%20world');
// → 'https://example.com/search?q=hello world'
Python:
from urllib.parse import unquote, unquote_plus
unquote('hello%20world%20%26%20more')
# → 'hello world & more'
unquote_plus('hello+world')
# → 'hello world'
Common Mistakes
Double encoding: If you encode a URL and then encode it again, %20 becomes %2520 (% gets encoded to %25). Always encode once, at the point of construction — not at the point of use.
Encoding structural characters: If you encode the ? or & in ?q=hello&page=1, the query string breaks entirely because the parser no longer sees the structural delimiters. Encode only the values, not the URL skeleton.
Not encoding at all: Constructing URLs by string concatenation without encoding is fragile. The moment a user types a &, =, or # in a search field, it breaks the URL.
Forgetting the fragment: The URL fragment (#section) is never sent to the server — it's client-side only. Don't encode it with the rest of the URL if you're building requests.
Quick Reference: When to Encode What
| Scenario | Use |
|---|---|
| Full URL going into a browser | encodeURI() |
| Query parameter value | encodeURIComponent() |
| HTML form submission | Browser handles it |
| API request parameter |
encodeURIComponent() / quote()
|
| Path segment with special chars |
encodeURIComponent() / quote()
|
Need to encode or decode a URL right now? SnappyTools URL Encoder/Decoder handles both RFC 3986 and form encoding in your browser — no install needed.
Top comments (0)