What Is URL Encoding (Percent-Encoding)?
URL encoding — formally called percent-encoding — is defined in RFC 3986, the specification that governs the syntax of Uniform Resource Identifiers. The rule is straightforward: any byte that is not permitted to appear literally in a URI must be expressed as a percent sign followed by exactly two uppercase hexadecimal digits. For example, a space (byte value 32, or 0x20 in hex) becomes %20.
RFC 3986 divides characters into two categories that can appear unencoded in a URI:
- Unreserved characters — always safe without encoding:
A–Z,a–z,0–9, hyphen-, underscore_, period., and tilde~. - Reserved characters — have special structural meaning in a URI and must be encoded when used as data:
: / ? # [ ] @ ! $ & ' ( ) * + , ; =
Everything else — including spaces, non-ASCII characters such as accented letters or CJK ideographs, and control characters — must be percent-encoded. For multi-byte Unicode characters the convention is to encode each byte of the UTF-8 representation separately. The character é (U+00E9) is encoded in UTF-8 as the two bytes 0xC3 0xA9, so it becomes %C3%A9.
Why URL Encoding Is Necessary
URLs are transmitted as ASCII text. Any character outside the printable ASCII range — or inside it but carrying structural meaning — can break the URL in one of three ways:
- Structural collision. Characters like
&,=, and?delimit query-string parameters. If a parameter value contains a raw&, the parser will split it into two parameters at that point. - Fragment collision. A raw
#inside a query value tells the browser that everything after it is a fragment identifier — the rest of the query is silently discarded before the request reaches the server. - Non-ASCII characters. HTTP requires headers and request lines to be ASCII. Non-ASCII bytes in a URL must be percent-encoded before transmission.
Proper encoding ensures that the server receives exactly the data you intended to send, regardless of what characters that data contains.
Characters That Must Be Encoded
The table below covers the characters you will most commonly need to encode when constructing query strings and URL values. The "When" column clarifies the context in which encoding is required.
| Character | Encoded As | When |
|---|---|---|
| Space | %20 (or + in form data) | Always — a raw space is not a valid URI character. In application/x-www-form-urlencoded bodies (RFC 1866) a + is also accepted, but %20 is the RFC 3986 form. |
| & | %26 | When used inside a query parameter value, not as the key=value separator. |
| = | %3D | When used inside a query parameter value, not as the key=value assignment operator. |
| + | %2B | In query strings, a bare + is interpreted as a space by form-data parsers, so a literal + must be encoded. |
| # | %23 | # marks the start of a fragment; a literal # inside a query value must be encoded or it will be treated as a fragment delimiter. |
| % | %25 | The percent sign is the escape character itself; a literal % that is not part of an encoding sequence must be encoded. |
| / | %2F | When used as data inside a path segment or query value, not as the path-component separator. |
| ? | %3F | When used inside a query value, not as the query-string delimiter. |
How to Encode a URL in JavaScript
JavaScript provides two built-in functions for URL encoding, and choosing the wrong one is a common source of bugs.
encodeURIComponent
encodeURIComponent(str) encodes everything except the unreserved characters defined in RFC 3986 plus ! ~ * ' ( ). This makes it correct for encoding individual query parameter names and values, because it will encode &, =, +, #, and all other reserved characters.
encodeURI
encodeURI(str) is intended for encoding a complete URL. It deliberately does not encode characters that are part of URI syntax: : / ? # [ ] @ ! $ & ' ( ) * + , ; =. Use it when you have a full URL whose structure you want to preserve but whose non-ASCII or invalid characters you want to sanitize.
JavaScript
// Encoding a single query parameter value — use encodeURIComponent
const value = 'hello world & more'
const encoded = encodeURIComponent(value)
console.log(encoded)
// → 'hello%20world%20%26%20more'
// Building a query string correctly
const params = new URLSearchParams({ q: 'café & tea', lang: 'fr' })
console.log(params.toString())
// → 'q=caf%C3%A9+%26+tea&lang=fr'
// Encoding a full URL while keeping its structure intact — use encodeURI
const url = 'https://example.com/search?q=hello world'
console.log(encodeURI(url))
// → 'https://example.com/search?q=hello%20world'
// Note: encodeURI does NOT encode & = ? : / so use encodeURIComponent
// for individual parameter values before assembling the URL.The practical rule: build your query values with encodeURIComponent (or URLSearchParams, which handles encoding internally), then concatenate them into the full URL. Never use encodeURI on a raw value that might contain & or =.
If you need to encode HTML entities as well as URL characters — for example when embedding a URL inside an HTML attribute — see our HTML Encoder / Decoder tool.
How to Encode a URL in Python
Python's standard library provides URL encoding through the urllib.parse module. The three functions you will use most often are:
urllib.parse.quote(string, safe='/')— encodes all characters except letters, digits, and_ . - ~, plus any characters listed in thesafeargument. The defaultsafe='/'keeps forward slashes unencoded so path segments remain intact. Passsafe=''to encode slashes too.urllib.parse.quote_plus(string)— likequotebut encodes spaces as+instead of%20. Use this for application/x-www-form-urlencoded data.urllib.parse.urlencode(query)— accepts a dict or list of pairs and returns a complete encoded query string usingquote_plusinternally (spaces become+).
Python
from urllib.parse import quote, quote_plus, urlencode
# RFC 3986 percent-encoding — spaces become %20
print(quote('hello world & more', safe=''))
# → 'hello%20world%20%26%20more'
# Encode a path segment — keep / unencoded (the default)
print(quote('/search/café latte'))
# → '/search/caf%C3%A9%20latte'
# Form-data style — spaces become +
print(quote_plus('hello world & more'))
# → 'hello+world+%26+more'
# Build a complete query string
params = {'q': 'hello world', 'lang': 'fr', 'page': 1}
print(urlencode(params))
# → 'q=hello+world&lang=fr&page=1'
# To get %20 instead of + in a query string, use quote with safe=''
# and assemble the string manually:
qs = '&'.join(f"{quote(k, safe='')}={quote(str(v), safe='')}"
for k, v in params.items())
print(qs)
# → 'q=hello%20world&lang=fr&page=1'How to Encode a URL in PHP
PHP ships three functions that cover URL encoding needs:
urlencode($str)— encodes for application/x-www-form-urlencoded: spaces become+. Use for query string values when working with HTML form submissions.rawurlencode($str)— encodes per RFC 3986: spaces become%20. Correct for URL path segments and for building RFC-compliant query strings.http_build_query(array $data)— encodes an associative array as a complete query string, handling all encoding internally using the same convention asurlencode.
PHP
<?php
// Form-data style — spaces become +
echo urlencode('hello world & more');
// → hello+world+%26+more
// RFC 3986 style — spaces become %20
echo rawurlencode('hello world & more');
// → hello%20world%20%26%20more
// Build a complete query string automatically
$params = ['key' => 'value', 'name' => 'hello world', 'page' => 2];
echo http_build_query($params);
// → key=value&name=hello+world&page=2
// Assemble a full URL with RFC 3986 encoding on each value
$base = 'https://example.com/search';
$qs = implode('&', array_map(
fn($k, $v) => rawurlencode($k) . '=' . rawurlencode($v),
array_keys($params),
array_values($params)
));
echo $base . '?' . $qs;
// → https://example.com/search?key=value&name=hello%20world&page=2Common Mistakes to Avoid
1. Double-encoding
Double-encoding happens when you run a percent-encoded string through an encoding function a second time. The % character is itself encoded as %25, so %20 becomes %2520. The server then decodes %2520 to the literal string %20 rather than a space. Always check whether a value is already encoded before encoding it again.
2. Encoding the entire URL instead of just the values
Passing a full URL such as https://example.com/search?q=hello into encodeURIComponent will encode the colons, slashes, and question mark, producing a mangled string like https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello. The fix is to encode only the parameter values and then assemble the URL — or use encodeURI which preserves structural characters.
3. Forgetting to encode + in query string values
In query strings decoded as application/x-www-form-urlencoded, a bare + is treated as a space. If you need to pass a literal plus sign as data, it must be encoded as %2B. Both encodeURIComponent in JavaScript and quote(safe='') in Python handle this correctly.
Frequently Asked Questions
What is the difference between encodeURI and encodeURIComponent in JavaScript?
encodeURI is designed for a complete URL: it leaves all characters that have structural meaning in a URI unencoded (including : / ? # [ ] @ & = +). encodeURIComponent is designed for an individual component value: it encodes everything except the unreserved characters. Use encodeURIComponent when encoding query parameter names and values; use encodeURI when sanitizing a pre-built URL you want to keep structurally intact.
Why must a space be encoded as %20 and not just left as a space?
RFC 3986 does not include the space character in the set of characters that may appear unencoded in a URI. A raw space in a URL causes parsers to truncate or misinterpret the request. The correct percent-encoded form is %20. In application/x-www-form-urlencoded bodies (HTML form POST, as described in RFC 1866) a space may also be represented as +, but that convention applies only to form data, not to URL paths.
What characters do NOT need to be percent-encoded in a URL?
RFC 3986 defines the unreserved character set as the only characters that are safe in a URI without any encoding: A–Z, a–z, 0–9, - (hyphen), _ (underscore), . (period), and ~ (tilde). Reserved characters may appear in their designated structural roles — for example / as a path separator — but must be encoded when used as data.
What is double-encoding and why is it a problem?
Double-encoding occurs when an already percent-encoded string is encoded a second time, turning %20 into %2520 because % encodes to %25. The server decodes the outer encoding and receives the literal string %20 rather than a space. The solution is to track whether data is already encoded and never encode it twice.
When should I use rawurlencode vs urlencode in PHP?
Use rawurlencode for RFC 3986-compliant encoding — it encodes spaces as %20 and is correct for URL path segments and RFC-compliant query strings. Use urlencode when building application/x-www-form-urlencoded data (HTML form POST), where spaces are conventionally represented as +. For building a full query string automatically, http_build_query handles encoding for you.
Related Tools & Guides
Letters to Numbers Converter
Convert letters to their numeric equivalents with our free online tool.
HTML Encoder / Decoder
Encode and decode HTML entities online — useful when embedding URLs inside HTML attributes.
What Is URL Encoding?
A comprehensive overview of percent-encoding, its history, and why it exists.
Online URL Decoder / Encoder
Instantly encode or decode any URL string in your browser — no install required.