
Listen to this article
Browser text-to-speech
What Is URL Encoding?
You have built a search feature, a user types "mac & cheese recipes" into the search box, and the resulting URL looks like this: /search?q=mac & cheese recipes. Everything after the ampersand vanishes. The query parameter silently breaks because the browser interprets & as a parameter separator, not as part of the search term.
URL encoding (also called percent-encoding) prevents exactly this kind of bug. It converts characters into a format that is safe to include in a URL by replacing them with a % sign followed by two hexadecimal digits representing the character's ASCII byte value.
Common examples:
- Space becomes
%20(or+in form data) &becomes%26?becomes%3F=becomes%3D#becomes%23
You can test these conversions yourself with our URL Encoder or convert characters to their underlying byte values with the Text to ASCII converter.
Why URL Encoding Exists
The RFC 3986 specification defines a strict set of characters that carry special meaning inside a URL:
?separates the path from query parameters&separates individual query parameters=separates a parameter name from its value/separates path segments#marks the start of a fragment identifier@separates user info from the host:separates the scheme, host, and port
When your actual data contains any of these characters, the browser or server has no way to tell whether the character is structural or literal. Percent-encoding resolves that ambiguity. A literal ampersand in data becomes %26, so parsers know it is not a parameter delimiter.
Without encoding, URLs break in subtle ways. A form submission might lose half its data. An API call might hit a completely different endpoint. A redirect URL embedded as a query parameter might hijack the entire request.
Characters That Must Be Encoded
Always Encode These
| Character | Encoded | Why |
|---|---|---|
| Space | %20 or + | Not valid in URLs |
& | %26 | Parameter separator |
+ | %2B | Interpreted as space in forms |
% | %25 | The escape character itself |
? | %3F | Query string delimiter |
# | %23 | Fragment delimiter |
= | %3D | Key-value separator |
Safe Without Encoding (Unreserved Characters)
These characters never need encoding:
- Letters:
A-Z,a-z - Digits:
0-9 - Four special characters:
-_.~
Reserved Characters (Context-Dependent)
These characters have structural meaning. Encode them when they appear as data, leave them unencoded when they serve their intended structural purpose:
: / ? # [ ] @ ! $ & ' ( ) * + , ; =
Common URL Encoding Mistakes
1. Double Encoding
Double encoding happens when you encode a string that is already encoded. The % sign itself gets encoded to %25, turning %20 into %2520.
Original: hello world
First pass: hello%20world ← correct
Second pass: hello%2520world ← broken
This is one of the most common bugs in web applications. It typically occurs when a framework encodes parameters automatically but the developer also encodes them manually. Always check whether your HTTP client or framework handles encoding for you before adding your own.
2. Not Encoding User Input
Any value that comes from a user, a database, or an external API must be encoded before being placed into a URL. Failing to encode user input is not just a bug; it is a security vulnerability. Unencoded input can enable cross-site scripting (XSS) or open redirect attacks.
// WRONG - user input inserted directly
const url = `/search?q=${userInput}`;
// CORRECT - user input encoded
const url = `/search?q=${encodeURIComponent(userInput)}`;
3. Confusing encodeURI and encodeURIComponent
JavaScript provides two encoding functions, and mixing them up causes real problems.
encodeURI encodes a complete URL. It leaves structural characters like ?, &, =, /, and # intact because it assumes they are part of the URL structure.
encodeURIComponent encodes a single value. It encodes everything except unreserved characters, including ?, &, and =.
const query = "price=10¤cy=USD";
encodeURI(query);
// "price=10¤cy=USD" — & and = left intact (WRONG for a single parameter value)
encodeURIComponent(query);
// "price%3D10%26currency%3DUSD" — fully encoded (CORRECT for a parameter value)
Rule of thumb: Use encodeURIComponent for individual parameter values. Use encodeURI only when you have a complete URL that just needs non-ASCII characters encoded.
When NOT to Encode
Not every string that touches a URL needs encoding. Over-encoding causes its own set of problems.
Do not encode path segments that are already known to be safe. If you are constructing a URL from hardcoded route segments like /api/v2/users, encoding those slashes would produce /api%2Fv2%2Fusers, which is a completely different URL.
Do not encode URLs that you will pass to fetch or XMLHttpRequest. Most HTTP clients handle encoding internally. Check your library's documentation first.
Do not encode the entire URL string at once. A URL has structure (scheme, host, path, query, fragment), and each part has different encoding rules. Encoding the whole string treats structural characters as data and breaks the URL.
Do not encode values you receive from URLSearchParams. The URLSearchParams API automatically decodes values when you read them and encodes them when you serialize. Adding your own encoding on top produces double-encoded output.
URL Encoding in Different Languages
JavaScript
// Encode a single parameter value
encodeURIComponent("hello world & goodbye");
// "hello%20world%20%26%20goodbye"
// Decode
decodeURIComponent("hello%20world%20%26%20goodbye");
// "hello world & goodbye"
// For building query strings, URLSearchParams handles encoding automatically
const params = new URLSearchParams({ q: "mac & cheese", page: "1" });
params.toString();
// "q=mac+%26+cheese&page=1"
Python
from urllib.parse import quote, unquote, urlencode
# Encode a single value
quote("hello world & goodbye")
# "hello%20world%20%26%20goodbye"
# Decode
unquote("hello%20world%20%26%20goodbye")
# "hello world & goodbye"
# Build a query string from a dictionary
urlencode({"q": "mac & cheese", "page": "1"})
# "q=mac+%26+cheese&page=1"
PHP
// Standard URL encoding (%20 for spaces)
rawurlencode("hello world & goodbye");
// "hello%20world%20%26%20goodbye"
// Form encoding (+ for spaces)
urlencode("hello world & goodbye");
// "hello+world+%26+goodbye"
// Decode
rawurldecode("hello%20world"); // "hello world"
urldecode("hello+world"); // "hello world"
PHP has two functions because there are two standards. rawurlencode follows RFC 3986 and uses %20 for spaces. urlencode follows the HTML form encoding spec and uses + for spaces. For URL path segments, use rawurlencode. For form data, either works.
Go
import "net/url"
// Encode a full query string
url.QueryEscape("hello world & goodbye")
// "hello+world+%26+goodbye"
// Encode a path segment
url.PathEscape("hello world")
// "hello%20world"
Java
import java.net.URLEncoder;
import java.net.URLDecoder;
// Encode (Java uses + for spaces like form encoding)
URLEncoder.encode("hello world & goodbye", "UTF-8");
// "hello+world+%26+goodbye"
// Decode
URLDecoder.decode("hello+world+%26+goodbye", "UTF-8");
// "hello world & goodbye"
Space Encoding: + vs %20
There are two valid ways to encode a space, and the difference matters.
%20 is the standard encoding defined by RFC 3986. It works everywhere in a URL: paths, query strings, fragments.
+ is specific to the application/x-www-form-urlencoded content type, which is what HTML forms submit by default. It is only valid in query strings.
Most servers accept both in query strings, but + in a URL path is treated as a literal plus sign, not a space. If you are building URLs programmatically, %20 is the safer choice because it is unambiguous in every position. Use our URL Decoder to see how different encodings resolve.
UTF-8 and International Characters
Modern URLs support characters from every language through UTF-8 encoding. When a URL contains non-ASCII characters like u, Chinese characters, or emoji, each character is first converted to its UTF-8 byte sequence, then each byte is percent-encoded individually.
| Character | Unicode | UTF-8 Bytes | Encoded |
|---|---|---|---|
| u | U+00FC | 0xC3 0xBC | %C3%BC |
| zhong (middle) | U+4E2D | 0xE4 0xB8 0xAD | %E4%B8%AD |
| party popper | U+1F389 | 0xF0 0x9F 0x8E 0x89 | %F0%9F%8E%89 |
A single character can expand to anywhere from 2 to 12 characters in its encoded form. This is why URLs with international text can look intimidatingly long, but they decode back to the original characters reliably.
Browsers help with this. Modern browsers display the decoded Unicode characters in the address bar while sending the encoded version over the network. So a user sees example.com/cafe even though the actual request uses example.com/caf%C3%A9.
Debugging Encoded URLs
When a URL is not working as expected, encoding is often the culprit. Here is a quick debugging checklist:
- Decode the URL first. Paste it into our URL Decoder to see what the server actually receives.
- Look for
%25. That is an encoded percent sign, which usually means double encoding happened somewhere. - Check for unencoded spaces. Spaces in URLs should be
%20or+, never a literal space. Some tools silently replace spaces, others reject the URL entirely. - Verify the encoding function. Are you using
encodeURIComponent(for values) orencodeURI(for full URLs)? The wrong one will either over-encode or under-encode. - Inspect the raw HTTP request. Browser DevTools (Network tab) shows the actual encoded URL sent to the server, which can differ from what the address bar displays.
Key Takeaways
- URL encoding replaces unsafe characters with
%XXhex sequences to prevent ambiguity in URL parsing. - Use
encodeURIComponentfor parameter values,encodeURIfor complete URLs. - Watch for double encoding, the most common encoding bug in web applications.
- Prefer
%20over+for spaces unless you are specifically working with form data. - Non-ASCII characters are encoded as their UTF-8 byte sequences.
- When in doubt, decode the URL to see what the server actually receives.
See what our calculators can do for you
Ready to take control of your finances?
Explore our free financial calculators and tools to start making informed decisions today.
Explore Our Tools