URL encode text and query parameters
URLs may only contain a limited set of ASCII characters. To URL encode a value, every other byte is written as a percent sign followed by two hex digits — %20 for a space, %C3%A9 for “é” — a scheme called percent-encoding and defined in RFC 3986. This tool does it for you, with four modes that match what browsers and server frameworks actually do.
How percent-encoding works
- The text is converted to bytes using UTF-8. ASCII characters are one byte; “é” is two bytes (C3 A9); an emoji is four.
- Bytes that are “unreserved” —
A–Z a–z 0–9 - . _ ~— are left as they are. - Every other byte becomes
%plus its value in hexadecimal, so a space becomes%20and&becomes%26.
RFC 3986 also defines reserved characters — :/?#[]@ and !$&'()*+,;= — which separate the parts of a URL. Whether they must be encoded depends on where the text goes, which is exactly what the modes below control.
The four modes
| Mode | Behaves like | Use it for |
|---|---|---|
| Component | encodeURIComponent() | A single query value, path segment or fragment. Encodes every reserved character except !'()*. |
| Full URL | encodeURI() | A complete URL that already has its structure. Keeps reserved characters such as :/?#&= and encodes spaces, non-ASCII text, % and unsafe characters such as "<>{}|\^. |
| Form data | HTML form submission | application/x-www-form-urlencoded bodies and query strings built by forms, where a space is +. |
| RFC 3986 strict | encodeURIComponent() + !'()* | OAuth 1.0 signatures, AWS Signature V4 and other specs that require the strict unreserved set. |
Example
The same input encoded in each mode:
https://example.com/a b?q=café & crème&x=it's(1)*| Mode | Output |
|---|---|
| Component | https%3A%2F%2Fexample.com%2Fa%20b%3Fq%3Dcaf%C3%A9%20%26%20cr%C3%A8me%26x%3Dit's(1)* |
| Full URL | https://example.com/a%20b?q=caf%C3%A9%20&%20cr%C3%A8me&x=it's(1)* |
| Form data | https%3A%2F%2Fexample.com%2Fa+b%3Fq%3Dcaf%C3%A9+%26+cr%C3%A8me%26x%3Dit's(1)* |
| RFC 3986 | https%3A%2F%2Fexample.com%2Fa%20b%3Fq%3Dcaf%C3%A9%20%26%20cr%C3%A8me%26x%3Dit%27s%281%29%2A |
Notice that Full URL mode left the & in “café & crème” alone, so a server would split the query there. That is why you should encode each value in Component mode before assembling the URL, rather than encoding the finished URL.
Common mistakes
- Double encoding. Encoding an already encoded string turns
%20into%2520. If you see%25everywhere, one layer too many was applied — decode once with the URL Decoder. - Using Full URL mode for values.
encodeURIdoes not encode&,=,?or#, so a value containing them breaks the query string. - Mixing up + and %20. In a query string produced by a form,
+means space. In a path,+is a literal plus. Encode a real plus sign as%2B. - Forgetting UTF-8. Old systems sometimes percent-encode Latin-1 bytes (é as %E9). Modern browsers and this tool always use UTF-8, as the WHATWG URL standard requires.
URL encoding in code
JavaScript has encodeURIComponent and URLSearchParams (which uses + for spaces). In Python use urllib.parse.quote for paths and urlencode for query strings; in PHP, rawurlencode follows RFC 3986 while urlencode uses +; Java’s URLEncoder.encode produces form encoding. Knowing which one a system uses explains most “why is my space a plus?” bugs.
To inspect the parts of a finished address, paste it into the URL Parser. For structured payloads, JSON URL Encode validates the JSON before encoding it. Encoding runs entirely in your browser.
URL encode in Python
urllib.parse.quote() percent-encodes a string as UTF-8. Its default catches people out: / is treated as safe and left alone, which suits paths but breaks a value that contains a slash. Pass safe="" when you encode a single query value or path segment. quote_plus() writes spaces as + for form data, and urlencode() builds a whole query string from a dict, encoding each key and value for you.
from urllib.parse import quote, quote_plus, urlencode
value = "a/b café & crème"
print(quote(value)) # "/" is left alone by default
print(quote(value, safe="")) # encode "/" too: use this for a single value
print(quote_plus(value)) # form style, space becomes +
# build a whole query string from a dict
params = {"q": "café & crème", "page": 2}
print("https://example.com/search?" + urlencode(params))
For this input, quote(value, safe="") matches the RFC 3986 strict mode of the encoder above and quote_plus() matches Form data; paste the result into the URL Decoder to check the round trip.