URL Encoding & Percent-Encoding: Complete Guide with Examples
URL encoding (percent-encoding) is the mechanism that converts characters unsafe for use in URLs β such as spaces, ampersands, and non-ASCII letters β into a portable %XX hexadecimal format defined by RFC 3986. A space becomes %20, an & becomes %26, and the Cyrillic Π becomes %D0%94. Every web developer encounters percent-encoding eventually, usually when a query parameter breaks or a redirect URL silently corrupts data.
This guide explains why URL encoding exists, which characters need encoding, the critical difference between encodeURI and encodeURIComponent, how to encode in JavaScript/Python/PHP/Go/Java, and the most common mistakes that cause double-encoding bugs and security vulnerabilities. Use our URL Encoder and URL Decoder to try it hands-on as you read.
Why URL Encoding Exists
URLs were designed to be transmitted using the ASCII character set. But the real world has spaces, accented characters, emoji, and special characters that have reserved meaning in URLs (like ?, &, =, #).
Percent-encoding solves this by replacing unsafe characters with % followed by two hexadecimal digits representing the characterβs byte value:
| Character | Encoded | Reason |
|---|---|---|
| Space | %20 | Not allowed in URLs |
& | %26 | Separates query parameters |
= | %3D | Separates key from value |
? | %3F | Starts the query string |
# | %23 | Starts the fragment |
/ | %2F | Path separator |
+ | %2B | Sometimes used for spaces |
@ | %40 | Used in authority component |
% | %25 | The escape character itself |
The encoding is defined in RFC 3986 Β§2.1, which is the authoritative specification for URI syntax.
Which Characters Need Encoding?
RFC 3986 defines these characters as unreserved (never need encoding):
A-Z a-z 0-9 - _ . ~
Everything else should be percent-encoded when used in URL components. This includes:
- Reserved characters:
: / ? # [ ] @ ! $ & ' ( ) * + , ; = - Spaces and whitespace
- Non-ASCII characters: accented letters, CJK characters, emoji
- Control characters
Reserved characters can appear unencoded in the URL parts where they have their defined meaning (for example, ? in the query delimiter position). But if you want to use ? as data inside a query parameter value, it must be encoded as %3F.
encodeURI vs encodeURIComponent
JavaScript provides two encoding functions with different scopes:
encodeURI() β encodes a complete URI. Preserves characters that have meaning in the URL structure:
encodeURI('https://example.com/path?q=hello world&lang=en')
// "https://example.com/path?q=hello%20world&lang=en"
// Note: ://?&= are NOT encoded
encodeURIComponent() β encodes a single URI component (typically a query parameter value). Encodes everything except unreserved characters:
encodeURIComponent('hello world&lang=en')
// "hello%20world%26lang%3Den"
// Note: & and = ARE encoded
Rule of thumb: Use encodeURIComponent() for individual values. Use encodeURI() only when you have a complete URL that just needs non-ASCII characters encoded.
Building URLs Safely
The safest way to build URLs with query parameters in modern JavaScript is the URL and URLSearchParams APIs:
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'hello world & more');
url.searchParams.set('page', '1');
url.toString()
// "https://example.com/search?q=hello+world+%26+more&page=1"
This handles all encoding automatically and correctly β no manual encodeURIComponent calls needed.
The + vs %20 Confusion
There are two ways to encode a space in URLs:
%20β standard percent-encoding (RFC 3986)+β used inapplication/x-www-form-urlencodedformat (WHATWG URL Standard)
The + encoding comes from HTML forms. When a form is submitted with method="GET", spaces in form values are encoded as + in the query string. This is a separate standard from general URL encoding.
// Standard URL encoding
encodeURIComponent('hello world') // "hello%20world"
// Form encoding
new URLSearchParams({q: 'hello world'}).toString() // "q=hello+world"
Both are valid in query strings, but %20 is the universally safe choice. Some servers and APIs donβt correctly decode + as a space outside of form submissions.
Encoding Non-ASCII Characters (UTF-8)
Non-ASCII characters are first converted to their UTF-8 byte sequence, then each byte is percent-encoded:
| Character | UTF-8 Bytes | Percent-Encoded |
|---|---|---|
Γ© | 0xC3 0xA9 | %C3%A9 |
ζ₯ | 0xE6 0x97 0xA5 | %E6%97%A5 |
π | 0xF0 0x9F 0x9A 0x80 | %F0%9F%9A%80 |
Modern browsers display non-ASCII characters natively in the address bar (a feature called IRI β Internationalized Resource Identifiers). But the actual HTTP request always uses the percent-encoded ASCII form.
encodeURIComponent('cafΓ©') // "caf%C3%A9"
encodeURIComponent('ζ₯ζ¬θͺ') // "%E6%97%A5%E6%9C%AC%E8%AA%9E"
Encoding in Different Languages
Python:
from urllib.parse import quote, unquote, urlencode
quote('hello world') # 'hello%20world'
quote('hello world', safe='') # 'hello%20world' (encode everything)
unquote('hello%20world') # 'hello world'
urlencode({'q': 'hello world'}) # 'q=hello+world'
PHP:
rawurlencode('hello world'); // "hello%20world"
urlencode('hello world'); // "hello+world"
rawurldecode('%E4%B8%AD'); // "δΈ"
Go:
url.QueryEscape("hello world") // "hello+world"
url.PathEscape("hello world") // "hello%20world"
Java:
URLEncoder.encode("hello world", StandardCharsets.UTF_8) // "hello+world"
// For %20 encoding, use URI class:
new URI("https", "example.com", "/path", "q=hello world", null).toASCIIString()
Bash (curl):
# curl encodes URLs with --data-urlencode
curl -G --data-urlencode "q=hello world" https://example.com/search
# Using printf + sed for manual encoding
printf '%s' "hello world" | jq -sRr @uri # "hello%20world"
Common Mistakes
Double encoding. Encoding an already-encoded string produces %2520 instead of %20 (the % gets encoded to %25). Always encode raw values, never pre-encoded strings. If youβre unsure whether a string is already encoded, decode it first, then re-encode.
Not encoding path segments. File paths with spaces or special characters must be encoded: /files/my document.pdf should be /files/my%20document.pdf.
Using the wrong function. encodeURI on a query parameter value wonβt encode & and =, breaking the query string. Always use encodeURIComponent for individual values.
Forgetting to decode on the server. Most web frameworks auto-decode URL parameters, but if youβre parsing raw URLs, remember to decode percent-encoded values before using them.
Encoding the entire URL with encodeURIComponent. This breaks the URL structure by encoding ://, /, ?, and &. Use encodeURIComponent only for individual values, not for complete URLs.
Not handling + correctly. Some backend frameworks decode + as a space only in query strings (form-encoded), but not in path segments. If your path contains a literal +, it must remain as %2B in the URL. Test your serverβs behavior.
URL Encoding and Security
Improper URL encoding can lead to security vulnerabilities:
Open redirects. If your application takes a redirect_url parameter without validating it, an attacker could use encoded characters to bypass URL validation: %2F%2Fevil.com decodes to //evil.com, which some browsers interpret as a protocol-relative URL.
Path traversal. Encoded ../ sequences (%2E%2E%2F) might bypass naive path filters. Always decode URLs before checking for path traversal patterns.
Log injection. URL-encoded newlines (%0A, %0D) can inject fake log entries if URLs are logged without sanitization.
The defense is straightforward: always validate and sanitize after decoding, never before.
Percent-Encoding Quick Reference
Here are the most commonly searched percent-encoded characters and their decoded equivalents:
| Character | Percent-Encoded | Unicode Name | Common Context |
|---|---|---|---|
| (space) | %20 or + | Space | Query strings, form data |
! | %21 | Exclamation mark | Passwords, expressions |
# | %23 | Hash / Number sign | Fragment identifiers |
$ | %24 | Dollar sign | Financial values |
% | %25 | Percent sign | The encoding character itself |
& | %26 | Ampersand | Query parameter separator |
+ | %2B | Plus sign | Math, phone numbers |
, | %2C | Comma | Lists in parameters |
/ | %2F | Forward slash | Path separator |
: | %3A | Colon | Protocols, ports |
; | %3B | Semicolon | Parameter delimiters |
= | %3D | Equals sign | Key-value pairs |
? | %3F | Question mark | Query string start |
@ | %40 | At sign | Email, authority |
[ | %5B | Left bracket | IPv6, arrays |
] | %5D | Right bracket | IPv6, arrays |
{ | %7B | Left brace | JSON in URLs |
} | %7D | Right brace | JSON in URLs |
| | %7C | Pipe | Delimiters |
(newline) | %0A | Line feed | Log injection attacks |
(tab) | %09 | Horizontal tab | Data alignment |
For non-ASCII characters, see the UTF-8 encoding section above. For a complete printable reference, see our URL Encoding Cheat Sheet.
Try It Yourself
Use our URL Encoder and URL Decoder to encode or decode text for URLs β processed entirely in your browser. For encoding HTML special characters, check out the HTML Entity Encoder.
For a compact printable reference of all percent-encoded characters, grab the URL Encoding Cheat Sheet.
You might also find our guide on URL slug best practices useful for creating SEO-friendly URLs.
Further Reading
- RFC 3986 β Uniform Resource Identifier (URI): Generic Syntax
- WHATWG URL Standard β Living standard for URL parsing and serialization
- RFC 3987 β Internationalized Resource Identifiers (IRIs)
- MDN: encodeURIComponent() β JavaScript encoding reference
- OWASP: URL Encoding β Security considerations for URL handling