Web DevelopmentMay 14, 2026

How To URL Encode

How To URL Encodea string means replacing characters that are not safe in a URL with a percent sign followed by two hexadecimal digits representing that character's byte value — a mechanism formally called percent-encoding and defined in RFC 3986. Whether you are building query strings in JavaScript, constructing API requests in Python, or assembling form submissions in PHP, understanding the rules of percent-encoding keeps your URLs valid and your data intact.

how to url encode — percent encoding diagram showing special characters converted to %XX sequences

What Is URL Encoding (Percent-Encoding)?

Percent-encoding is defined in RFC 3986, the specification that governs the syntax of Uniform Resource Identifiers. The rule is precise: any byte that is not permitted to appear literally in a URI must be represented as a percent sign %followed by exactly two uppercase hexadecimal digits giving that byte's value. For example, a space has the byte value 32 decimal, which is 0x20 in hexadecimal, so it becomes %20.

RFC 3986 divides characters into two groups that may appear unencoded:

  • Unreserved characters — always safe without encoding: A–Z, a–z, 0–9, hyphen -, underscore _, period ., and tilde ~.
  • Reserved characters — carry structural meaning in a URI and must be encoded when used as data: ; : @ & = + $ , / ? # [ ] !

Everything else — spaces, non-ASCII characters, and control characters — must be percent-encoded. For multi-byte Unicode characters, RFC 3986 requires encoding each byte of the UTF-8 representation separately. The pound sign £ (U+00A3) is encoded in UTF-8 as two bytes 0xC2 0xA3, so it becomes %C2%A3.

Characters That Must Be Encoded

The table below lists the characters you will encounter most often when constructing query strings and URL values, along with their percent-encoded equivalents and context notes.

CharacterEncoded AsNotes
Space%20RFC 3986 form. In application/x-www-form-urlencoded (HTML form POST), a space may also be encoded as +, but %20 is correct for URL paths and RFC-compliant query strings.
&%26Must be encoded inside a query parameter value; left unencoded it acts as the key=value pair separator.
=%3DMust be encoded inside a query parameter value; left unencoded it acts as the key=value assignment operator.
+%2BIn query strings decoded as form data, a bare + is interpreted as a space. A literal plus sign must be encoded as %2B.
#%23# marks the start of a fragment identifier. A literal # inside a query value must be encoded or everything after it is discarded by the browser before reaching the server.
%%25The percent sign is the escape character itself. A literal % that is not part of a valid percent-encoded sequence must be encoded as %25.
/%2FMust be encoded when used as data inside a path segment or query value; left unencoded it is the path component separator.
?%3FMust be encoded when used inside a query value; left unencoded it marks the start of the query string.
@%40Has structural meaning in the authority component (user@host). Must be encoded when used as data in a query value or path segment.
:%3ASeparates the scheme from the host (https:) and the host from the port. Must be encoded when used as literal data.

How URL Encoding Works Step by Step

The algorithm defined in RFC 3986 is straightforward. For each character in the string:

  1. Check if it is an unreserved character (A–Z, a–z, 0–9, -, _, ., ~). If so, leave it as-is.
  2. Otherwise, convert the character to its UTF-8 byte sequence. For ASCII characters this is a single byte. For characters outside the ASCII range it may be two, three, or four bytes.
  3. Percent-encode each byte. Write a %followed by the two uppercase hexadecimal digits of that byte's value.

Two concrete examples illustrate this process well. First, a space character: its ASCII (and UTF-8) byte value is 0x20, so it encodes to %20 — just one byte, one percent-encoded triplet.

Second, the pound sign £ (U+00A3): it lies outside ASCII, so we need its UTF-8 encoding. U+00A3 is encoded in UTF-8 as two bytes: 0xC2 and 0xA3. Each byte is percent-encoded in turn, producing %C2%A3. A string like price: £10 & tax therefore becomes price%3A%20%C2%A310%20%26%20tax.

How To URL Encode in JavaScript

JavaScript provides two built-in functions for URL encoding, and picking the wrong one is the most common source of encoding bugs in front-end and Node.js code.

encodeURIComponent

encodeURIComponent(str) encodes everything except the unreserved characters A–Z a–z 0–9 - _ . ! ~ * ' ( ). This means it will encode &, =, +, #, :, and / — exactly what you need when encoding individual query parameter names and values before assembling them into a URL.

encodeURI

encodeURI(str) is intended for a complete URL. It deliberately does not encode characters that have structural meaning: : / ? # [ ] @ ! $ & ' ( ) * + , ; =. Use it when you already have a well-formed URL and simply want to sanitize any non-ASCII characters without disturbing the URL's structure.

The inverse functions are decodeURIComponent(str) and decodeURI(str). The legacy escape() function is deprecated — it does not handle non-ASCII characters correctly and must not be used.

JavaScript

// Encode an individual query parameter value — use encodeURIComponent
const value = 'price: £10 & tax'
console.log(encodeURIComponent(value))
// → 'price%3A%20%C2%A310%20%26%20tax'

// Build a safe query string using URLSearchParams (handles encoding internally)
const params = new URLSearchParams({ q: 'café & tea', lang: 'fr' })
console.log(params.toString())
// → 'q=caf%C3%A9+%26+tea&lang=fr'

// Encode a full URL while keeping its structure — use encodeURI
const url = 'https://example.com/search?q=hello world'
console.log(encodeURI(url))
// → 'https://example.com/search?q=hello%20world'

// Decode a percent-encoded string
console.log(decodeURIComponent('price%3A%20%C2%A310%20%26%20tax'))
// → 'price: £10 & tax'

// DO NOT use the deprecated escape() function
// escape('café') → 'caf%E9'  ← wrong: not UTF-8, not RFC 3986

The practical rule: encode each query parameter name and value individually with encodeURIComponent (or let URLSearchParams do it for you), then join them with & and append to the base URL. Never pass a complete URL into encodeURIComponent.

For a deeper look at the broader topic, see our guide on what is URL encoding.

How To URL Encode in Python

Python's urllib.parse module provides three functions that cover the full range of URL encoding needs:

  • urllib.parse.quote(str, safe='/') — RFC 3986-compliant; spaces become %20. The safe argument lists characters that should not be encoded (forward slash by default). Pass safe='' to encode slashes too.
  • urllib.parse.quote_plus(str) — like quote but encodes spaces as + instead of %20. Use for individual query string values in application/x-www-form-urlencoded data.
  • urllib.parse.urlencode(dict) — encodes an entire dictionary into a complete query string, using quote_plus internally (spaces become +).

Python

from urllib.parse import quote, quote_plus, urlencode

# RFC 3986 — spaces become %20
print(quote('price: £10 & tax', safe=''))
# → 'price%3A%20%C2%A310%20%26%20tax'

# Encode a URL path segment — keep / unencoded (the default safe value)
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 from a dict
params = {'q': 'hello world', 'lang': 'fr', 'page': 1}
print(urlencode(params))
# → 'q=hello+world&lang=fr&page=1'

# RFC 3986 query string with %20 instead of +
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 URL Encode in PHP

PHP provides three URL encoding functions, each suited to a different use case:

  • rawurlencode($str) — RFC 3986 compliant; spaces become %20. Use for URL path segments and RFC-compliant query string values.
  • urlencode($str) — encodes for application/x-www-form-urlencoded; spaces become +. Use for query string values in HTML form submissions.
  • http_build_query($array) — builds a complete query string from an associative array, handling all encoding internally (spaces become +).

PHP

<?php

// RFC 3986 style — spaces become %20
echo rawurlencode('price: £10 & tax');
// → price%3A%20%C2%A310%20%26%20tax

// Form-data style — spaces become +
echo urlencode('price: £10 & tax');
// → price%3A+%C2%A310+%26+tax

// Build a complete query string from an array
$params = ['q' => 'hello world', 'lang' => 'fr', 'page' => 2];
echo http_build_query($params);
// → q=hello+world&lang=fr&page=2

// Assemble a URL with RFC 3986 encoding on each value
$base = 'https://example.com/search';
$qs = implode('&', array_map(
    fn($k, $v) => rawurlencode((string)$k) . '=' . rawurlencode((string)$v),
    array_keys($params),
    array_values($params)
));
echo $base . '?' . $qs;
// → https://example.com/search?q=hello%20world&lang=fr&page=2

You can find more encoding examples in our guide on how to encode PHP code. For a broader look at how to encode HTML alongside URL values, see how to encode HTML code.

Common URL Encoding Mistakes to Avoid

1. Encoding the entire URL instead of just the values

Passing a complete URL such as https://example.com/search?q=hello into encodeURIComponent encodes 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 individual parameter values, then assemble the URL — or to use encodeURI, which preserves structural characters.

2. Double-encoding

Double-encoding occurs when you run an already percent-encoded string through an encoding function a second time. Because % itself encodes to %25, the correctly encoded space %20 becomes %2520 on a second pass. The server then decodes %2520 back to the string %20 rather than a space. Always track whether your data is already encoded before encoding it again.

3. Using the deprecated escape() in JavaScript

The escape() function is deprecated and must not be used. It does not encode non-ASCII characters as UTF-8 bytes — instead it uses a non-standard %uXXXX format for code points above 127, which is not recognised by modern servers or the RFC 3986 specification.

4. Confusing + with %20 in URL paths vs query strings

A + sign in a URL path is a literal plus sign. In a query string decoded as application/x-www-form-urlencoded (the convention for HTML form submissions), a + is interpreted as a space. This means a literal plus sign in a query string value must be encoded as %2B, and you should use %20 (not +) for spaces in URL paths to avoid ambiguity.

Frequently Asked Questions

What is the difference between encodeURI and encodeURIComponent in JavaScript?

encodeURIComponent encodes everything except unreserved characters, making it correct for individual query parameter names and values. encodeURI is designed for a complete URL and leaves structural characters such as : / ? # [ ] @ & = + unencoded. Use encodeURIComponent on values, not full URLs.

Why must a space be encoded as %20 and not left as a raw 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 the URL or return a 400 Bad Request error. %20 is the correct percent-encoded form. In application/x-www-form-urlencoded bodies, a space may be encoded 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: A–Z, a–z, 0–9, - (hyphen), _ (underscore), . (period), and ~ (tilde). These may appear in a URI without encoding. All other characters — including reserved characters when used as data — must be percent-encoded.

What is double-encoding and why is it a problem?

Double-encoding happens when a percent-encoded string is encoded a second time. Because % encodes to %25, the correctly encoded space %20 becomes %2520. The server decodes only one layer and receives the literal string %20 instead of a space.

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 submissions), where spaces are conventionally encoded as +. For a complete query string from an array, http_build_query handles encoding automatically.

Related Tools & Guides