If you've ever pasted a URL into a browser and seen %20 where a space should be, or watched an API call fail because a query parameter contained an ampersand, you've encountered URL encoding. Also known as percent-encoding, it's the mechanism that lets URLs carry special characters safely across the internet.
In this comprehensive guide, we'll cover everything you need to know about URL encoding and decoding: the underlying standard (RFC 3986), how percent-encoding works character by character, the differences between reserved and unreserved characters, practical code examples in JavaScript and Python, a complete reference table of common encodings, and the most frequent mistakes developers make. Whether you need a quick URL encoder decoder online free tool or want to deeply understand the mechanics, this article has you covered.
What Is URL Encoding (Percent-Encoding)?
URL encoding — formally called percent-encoding — is the process of converting characters into a format that can be safely transmitted within a Uniform Resource Locator. URLs are restricted to a specific set of ASCII characters. Any character outside that set, or any reserved character used outside its special purpose, must be encoded.
The encoding is straightforward: each byte of the character's UTF-8 representation is written as a percent sign (%) followed by two hexadecimal digits. For example, a space character (byte value 0x20) becomes %20. A multi-byte character like ü (UTF-8 bytes 0xC3 0xBC) becomes %C3%BC.
This mechanism ensures that every URL remains valid ASCII, even when it references resources with names in Chinese, Arabic, emoji, or any other Unicode script. It's the silent workhorse behind every link you click.
RFC 3986: The Standard Behind URL Encoding
The authoritative specification for URL syntax is RFC 3986 (published in 2005, updating the earlier RFC 2396). It defines the generic syntax for URIs (Uniform Resource Identifiers) and precisely specifies which characters are allowed in each part of a URL without encoding.
RFC 3986 divides characters into two groups:
Unreserved Characters (Never Need Encoding)
These characters can appear anywhere in a URL without being percent-encoded:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9
- . _ ~ That's 66 characters total: uppercase letters, lowercase letters, digits, hyphen, period, underscore, and tilde. Everything else either has a special meaning or must be encoded.
Reserved Characters (Special Meaning in URLs)
Reserved characters have specific roles in URL syntax. If you want to use them as literal data (for example, an ampersand in a company name), you must percent-encode them:
: / ? # [ ] @ ! $ & ' ( ) * + , ; =
For example, the ? character separates the path from the query string. If your query value itself contains a question mark, that question mark must be encoded as %3F to avoid ambiguity.
How Percent-Encoding Works Step by Step
Let's walk through encoding the string hello world & goodbye as a URL query parameter value:
- Identify characters needing encoding: The space (
) and ampersand (&) are not unreserved characters, so they must be encoded. The letters are unreserved and stay as-is. - Convert each character to UTF-8 bytes: Space =
0x20, ampersand =0x26. - Represent each byte as %HH: Space becomes
%20, ampersand becomes%26. - Result:
hello%20world%20%26%20goodbye
Decoding is the reverse: find every %HH sequence, convert the hex digits back to a byte value, and reconstruct the original character from the UTF-8 bytes. You can try both directions instantly with our free URL encoder decoder tool.
Complete URL Encoding Reference Table
Here are the most commonly encountered percent-encoded characters. Bookmark this table — you'll refer to it more often than you think:
Character Encoded Description
───────── ───────── ──────────────────────────
(space) %20 Space character
! %21 Exclamation mark
" %22 Double quote
# %23 Hash / fragment delimiter
$ %24 Dollar sign
% %25 Percent sign (literal)
& %26 Ampersand / query separator
' %27 Single quote / apostrophe
( %28 Open parenthesis
) %29 Close parenthesis
* %2A Asterisk
+ %2B Plus sign
, %2C Comma
/ %2F Forward slash / path separator
: %3A Colon
; %3B Semicolon
= %3D Equals sign
? %3F Question mark / query delimiter
@ %40 At sign
[ %5B Open bracket
] %5D Close bracket
{ %7B Open brace
| %7C Pipe
{ %7D Close brace
Notice that the percent sign itself encodes to %25. This is critical: if you double-encode a URL, every % from the first pass gets turned into %25, which is one of the most common bugs in URL handling.
URL Encoding in JavaScript: encodeURIComponent vs encodeURI
JavaScript provides two built-in functions for URL encoding, and using the wrong one is a frequent source of bugs. Understanding the difference is essential for any web developer.
encodeURIComponent()
This function encodes a component of a URI — typically a query parameter key or value. It encodes everything except unreserved characters (A-Z a-z 0-9 - _ . ~):
// Encoding a query parameter value
const searchTerm = "cats & dogs";
const encoded = encodeURIComponent(searchTerm);
console.log(encoded);
// Output: "cats%20%26%20dogs"
// Building a complete query string
const url = "https://example.com/search?q=" + encodeURIComponent(searchTerm);
console.log(url);
// Output: "https://example.com/search?q=cats%20%26%20dogs"
// Encoding special characters
console.log(encodeURIComponent("price=100¤cy=USD"));
// Output: "price%3D100%26currency%3DUSD"
Use encodeURIComponent() when you're encoding a value that will be placed into a URL. This is the function you'll use 90% of the time.
encodeURI()
This function encodes a complete URI. It preserves characters that have structural meaning in a URL (: / ? # [ ] @ ! $ & ' ( ) * + , ; =) and only encodes characters that are invalid in any part of a URL:
// Encoding a complete URL with Unicode characters
const url = "https://example.com/résumé?name=André Müller";
console.log(encodeURI(url));
// Output: "https://example.com/r%C3%A9sum%C3%A9?name=Andr%C3%A9%20M%C3%BCller"
// Notice: the : / ? = are preserved
// Only the accented characters and space are encoded
Use encodeURI() only when you have a complete URL that may contain non-ASCII characters but whose structure (slashes, colons, query delimiters) should remain intact.
Decoding in JavaScript
// decodeURIComponent — decodes a single component
console.log(decodeURIComponent("cats%20%26%20dogs"));
// Output: "cats & dogs"
// decodeURI — decodes a full URI, preserving structural characters
console.log(decodeURI("https://example.com/r%C3%A9sum%C3%A9?q=hello%20world"));
// Output: "https://example.com/résumé?q=hello world" The Critical Mistake: Using encodeURI on a Parameter Value
// WRONG — encodeURI does NOT encode & and =
const userInput = "tom&jerry=best";
const bad = "https://api.com/search?q=" + encodeURI(userInput);
console.log(bad);
// "https://api.com/search?q=tom&jerry=best"
// The server sees q=tom, jerry=best — completely wrong!
// CORRECT — encodeURIComponent encodes & and =
const good = "https://api.com/search?q=" + encodeURIComponent(userInput);
console.log(good);
// "https://api.com/search?q=tom%26jerry%3Dbest"
// The server correctly sees q=tom&jerry=best URL Encoding in Python: urllib.parse
Python's standard library provides robust URL encoding and decoding through the urllib.parse module. Here are the key functions:
quote() and quote_plus()
from urllib.parse import quote, quote_plus, urlencode
# quote() — similar to encodeURIComponent
print(quote("hello world & goodbye"))
# Output: "hello%20world%20%26%20goodbye"
# quote_plus() — same but encodes spaces as + instead of %20
# This is the application/x-www-form-urlencoded format
print(quote_plus("hello world & goodbye"))
# Output: "hello+world+%26+goodbye"
# Specifying safe characters (characters NOT to encode)
print(quote("path/to/file", safe="/"))
# Output: "path/to/file" (slashes preserved)
print(quote("path/to/file", safe=""))
# Output: "path%2Fto%2Ffile" (slashes encoded) unquote() and unquote_plus()
from urllib.parse import unquote, unquote_plus
# unquote() — decodes %XX sequences
print(unquote("hello%20world%20%26%20goodbye"))
# Output: "hello world & goodbye"
# unquote_plus() — also converts + back to spaces
print(unquote_plus("hello+world+%26+goodbye"))
# Output: "hello world & goodbye" urlencode() — Building Query Strings
from urllib.parse import urlencode
# Build a complete query string from a dictionary
params = {
"q": "cats & dogs",
"page": 1,
"lang": "en",
"filter": "price>=100"
{
query_string = urlencode(params)
print(query_string)
# Output: "q=cats+%26+dogs&page=1&lang=en&filter=price%3E%3D100"
# Use quote_via=quote to get %20 instead of + for spaces
from urllib.parse import quote
query_string = urlencode(params, quote_via=quote)
print(query_string)
# Output: "q=cats%20%26%20dogs&page=1&lang=en&filter=price%3E%3D100" URL Encoding in Other Languages
PHP
// urlencode() — spaces become +
echo urlencode("hello world & goodbye");
// Output: "hello+world+%26+goodbye"
// rawurlencode() — spaces become %20 (RFC 3986 compliant)
echo rawurlencode("hello world & goodbye");
// Output: "hello%20world%20%26%20goodbye"
// Decoding
echo urldecode("hello+world+%26+goodbye");
// Output: "hello world & goodbye" Java
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
// Encoding
String encoded = URLEncoder.encode("hello world & goodbye", StandardCharsets.UTF_8);
System.out.println(encoded);
// Output: "hello+world+%26+goodbye"
// Decoding
String decoded = URLDecoder.decode("hello%20world%20%26%20goodbye", StandardCharsets.UTF_8);
System.out.println(decoded);
// Output: "hello world & goodbye" C# / .NET
using System.Net;
using System;
// Uri.EscapeDataString — RFC 3986 compliant
string encoded = Uri.EscapeDataString("hello world & goodbye");
Console.WriteLine(encoded);
// Output: "hello%20world%20%26%20goodbye"
// WebUtility.UrlEncode — form encoding (+ for spaces)
string formEncoded = WebUtility.UrlEncode("hello world & goodbye");
Console.WriteLine(formEncoded);
// Output: "hello+world+%26+goodbye" Spaces in URLs: %20 vs + (Plus Sign)
One of the most confusing aspects of URL encoding is the dual representation of spaces. There are two conventions, and mixing them up causes subtle bugs:
%20— The RFC 3986 standard representation. A space is byte 0x20 in ASCII, so percent-encoding produces%20. This is correct for all parts of a URL: paths, fragments, and query strings.+— Theapplication/x-www-form-urlencodedformat, defined in the HTML specification. When an HTML form is submitted with the default encoding, spaces in form values are encoded as+. This convention is only valid inside query strings.
In practice, most servers accept both %20 and + in query strings. However, + in a URL path means a literal plus sign, not a space. This distinction has caused countless bugs in web applications.
// These are equivalent in a query string:
https://example.com/search?q=hello+world
https://example.com/search?q=hello%20world
// But in a path, + is literal:
https://example.com/files/C++/readme.txt // Path contains "C++"
https://example.com/files/C%2B%2B/readme.txt // Same path, percent-encoded When Should You URL Encode?
Understanding when to encode is just as important as knowing how. Here are the key scenarios:
1. Query Parameter Values
Always encode user-supplied values before inserting them into query strings. This is the single most common use case for URL encoding:
// User types: O'Brien & Sons (price > $100)
const input = "O'Brien & Sons (price > $100)";
const url = `/search?company=${encodeURIComponent(input){`;
// Result: /search?company=O'Brien%20%26%20Sons%20(price%20%3E%20%24100) 2. Path Segments with Special Characters
If a filename or directory name contains reserved characters, encode the individual path segment — not the entire path:
const filename = "report (final).pdf";
const url = `/documents/${encodeURIComponent(filename){`;
// Result: /documents/report%20(final).pdf 3. Non-ASCII Characters (Internationalized URLs)
URLs with characters outside the ASCII range must be encoded. Modern browsers display the decoded version in the address bar for readability, but the actual HTTP request uses the encoded form:
# What you see in the browser:
https://ja.wikipedia.org/wiki/東京
# What's actually sent over HTTP:
https://ja.wikipedia.org/wiki/%E6%9D%B1%E4%BA%AC 4. Building API Requests
When constructing API calls programmatically, always encode parameter values. API gateways and servers depend on proper encoding to parse requests correctly:
import requests
from urllib.parse import urlencode, quote
# Using requests library (handles encoding automatically)
response = requests.get("https://api.example.com/search", params={
"q": "café & restaurant",
"location": "Zürich"
{)
# Requests builds: /search?q=caf%C3%A9+%26+restaurant&location=Z%C3%BCrich Common URL Encoding Mistakes (And How to Fix Them)
After years of debugging URL-related issues, these are the most frequent problems developers encounter. Avoid these pitfalls to save yourself hours of frustration.
Mistake 1: Double Encoding
This is the number one URL encoding bug. It happens when you encode a string that's already been encoded, turning %20 into %2520:
// The bug
const alreadyEncoded = "hello%20world";
const doubleEncoded = encodeURIComponent(alreadyEncoded);
console.log(doubleEncoded);
// "hello%2520world" — BROKEN! %25 is the encoding of %
// The fix: only encode raw values, never encoded strings
// If you're unsure whether a string is already encoded,
// decode first, then re-encode:
const safeEncode = (str) => {
try {
return encodeURIComponent(decodeURIComponent(str));
{ catch (e) {
return encodeURIComponent(str);
{
{; Mistake 2: Not Encoding Query Parameter Values
String concatenation without encoding is dangerously common and can break functionality or create security vulnerabilities:
// WRONG — if userInput contains & or =, the URL breaks
const url = `https://api.com/data?name=${userInput{`;
// CORRECT — always encode
const url = `https://api.com/data?name=${encodeURIComponent(userInput){`; Mistake 3: Encoding the Entire URL
Encoding a complete URL with encodeURIComponent() destroys its structure:
// WRONG
const broken = encodeURIComponent("https://example.com/path?key=value");
// "https%3A%2F%2Fexample.com%2Fpath%3Fkey%3Dvalue" — not a valid URL!
// CORRECT — encode only the parts that need it
const url = "https://example.com/path?key=" + encodeURIComponent(userValue); Mistake 4: Forgetting to Decode on the Server
If your server receives caf%C3%A9 and stores it without decoding, you end up with literal percent signs in your database. Most web frameworks handle this automatically, but raw HTTP parsing or custom routing may not.
Mistake 5: Mixing + and %20 Incorrectly
Using + for spaces in URL paths (where it means a literal plus sign) or using %20 in form-encoded bodies (where the convention expects +) causes mismatches between what you send and what the server interprets.
Debugging Encoded URLs
When something goes wrong with a URL, here's a systematic approach to debugging:
- Decode the URL using our URL encoder decoder online free tool to see the plain-text version. Visually inspect whether it matches what you expect.
- Check for double encoding: Look for
%25sequences. If you see%2520,%253A, or similar, the URL was encoded twice. - Inspect the raw HTTP request: Use browser DevTools (Network tab) or a proxy like Charles/Fiddler to see exactly what bytes were sent. The browser's address bar may show a decoded version for readability.
- Verify each component separately: Break the URL into scheme, host, path, query key-value pairs, and fragment. Check that encoding was applied at the right level.
- Test with known values: Send a request with a known special character (like
&) in a parameter and verify the server receives it correctly.
For quickly decoding and re-encoding URLs during debugging, use our free online URL encoder/decoder. You might also find our JSON formatter helpful when inspecting API response bodies that contain encoded URLs, or our Base64 encoder when dealing with Base64-encoded URL parameters.
Query String Encoding in Detail
Query strings follow the format ?key1=value1&key2=value2. Properly encoding them requires attention to several details:
// Building query strings correctly in JavaScript
function buildQueryString(params) {
return Object.entries(params)
.map(([key, value]) =>
`${encodeURIComponent(key){=${encodeURIComponent(value){`
)
.join('&');
{
const params = {
search: "cats & dogs",
page: "2",
filter: "price>=10",
tag: "cute/funny"
{;
console.log(buildQueryString(params));
// search=cats%20%26%20dogs&page=2&filter=price%3E%3D10&tag=cute%2Ffunny
Modern JavaScript also provides the URLSearchParams API, which handles encoding automatically:
const params = new URLSearchParams({
search: "cats & dogs",
filter: "price>=10"
{);
console.log(params.toString());
// "search=cats+%26+dogs&filter=price%3E%3D10"
// Note: URLSearchParams uses + for spaces (form encoding convention)
// Building a complete URL
const url = new URL("https://example.com/search");
url.searchParams.set("q", "hello world & goodbye");
console.log(url.toString());
// "https://example.com/search?q=hello+world+%26+goodbye" URL Encoding and Security
URL encoding intersects with security in several important ways:
- Injection prevention: Proper URL encoding prevents query string injection, where an attacker-controlled value like
value&admin=truecould add unauthorized parameters. Encoding the&as%26neutralizes this. - Path traversal: Encoding
../as%2E%2E%2Fin user input prevents path traversal attacks. However, you must ensure your server doesn't decode the path before checking for traversal sequences. - Open redirect: URLs in redirect parameters should be validated after decoding, not before. An attacker might encode a malicious URL to bypass string-matching filters.
- XSS via URLs: URL encoding alone does not prevent cross-site scripting. Always use context-appropriate output encoding (HTML encoding for HTML contexts, JavaScript encoding for JS contexts) in addition to URL encoding.
URL Encoding for International Domain Names (IDN)
While the path and query parts of a URL use percent-encoding for non-ASCII characters, domain names use a different system called Punycode (RFC 3492). For example:
// Human-readable:
https://münchen.de/straße?q=grüße
// Actual URL sent over HTTP:
// Domain uses Punycode: münchen.de → xn--mnchen-3ya.de
// Path uses percent-encoding: straße → stra%C3%9Fe
// Query uses percent-encoding: grüße → gr%C3%BC%C3%9Fe
https://xn--mnchen-3ya.de/stra%C3%9Fe?q=gr%C3%BC%C3%9Fe This is handled automatically by browsers and HTTP libraries, but it's important to understand when debugging internationalized URLs.
URL Length Limits and Encoding Overhead
Percent-encoding increases URL length: each encoded character becomes three characters (%XX), and multi-byte UTF-8 characters can expand even more. A single emoji like a thumbs-up sign is 4 UTF-8 bytes, which becomes 12 characters when percent-encoded (%F0%9F%91%8D).
While RFC 3986 doesn't define a maximum URL length, practical limits exist:
- Browsers: Chrome supports up to ~2MB, but Internet Explorer (legacy) was limited to 2,083 characters.
- Web servers: Apache defaults to 8,190 bytes, Nginx defaults to 8KB for the entire request line.
- CDNs and proxies: Cloudflare limits URLs to 16KB.
- Practical recommendation: Keep URLs under 2,000 characters for maximum compatibility.
If your encoded URL is too long, consider sending the data in a POST request body instead, or use shortened identifiers that the server can look up.
URL Encoding vs Other Encoding Schemes
URL encoding is one of several encoding systems developers encounter. Here's how they compare:
- URL encoding (percent-encoding): For URLs. Encodes bytes as
%HH. Use our URL encoder/decoder. - HTML encoding: For HTML content. Encodes characters like
<as<. Different purpose — prevents XSS, not URL breakage. - Base64 encoding: Binary-to-text encoding. Produces a longer string but uses only safe ASCII characters. Sometimes used for URL parameters (
base64urlvariant replaces+and/with-and_). - Unicode escaping: Language-specific (e.g.,
\u00E9in JavaScript). Used in source code, not URLs.
It's common to combine these: a JSON payload might be Base64-encoded and then URL-encoded to pass through a query string. Just be careful about the order of encoding and decoding.
Quick Reference: URL Encoding Cheat Sheet
Here's a concise reference for the most common URL encoding scenarios:
Scenario JavaScript Python
────────────────────────────── ───────────────────────────── ──────────────────────────
Encode a query value encodeURIComponent(val) quote(val)
Encode a full URL encodeURI(url) quote(url, safe=':/?#[]@!$&\'()*+,;=')
Encode spaces as + N/A (use URLSearchParams) quote_plus(val)
Build query string from dict new URLSearchParams(obj) urlencode(dict)
Decode a component decodeURIComponent(val) unquote(val)
Decode + as space decodeURIComponent( unquote_plus(val)
val.replace(/\+/g, ' '))
Decode a full URL decodeURI(url) unquote(url) Wrapping Up
URL encoding is a foundational web skill that sits at the intersection of standards compliance, security, and practical debugging. Whether you're building API integrations, handling form submissions, working with international content, or just trying to figure out why your query parameter is breaking, understanding percent-encoding will save you time and frustration.
The key takeaways:
- Use
encodeURIComponent()(or your language's equivalent) for parameter values — this is the right choice 90% of the time. - Never encode an already-encoded URL (double encoding is the most common bug).
- Know the difference between
%20and+for spaces, and when each applies. - Encode at the right level: individual values, not entire URLs.
- When debugging, always check the raw HTTP request, not the browser's decoded display.
Ready to encode or decode a URL right now? Head over to our free URL encoder decoder online tool — paste your string, get instant results, no sign-up required.