URL encoding (also called percent-encoding) converts bytes that have special meaning in a URL into a %XX form where XX is the byte’s hex value. The tool above encodes input in your browser using encodeURIComponent; nothing leaves your machine. To reverse the process, see the URL decoder.
Why encoding exists
URLs have structure. A character like ? separates the path from the query string. & separates query parameters. # introduces the fragment. / is a path separator. If the value you want to put inside a URL contains those characters, the parser can’t tell where the value ends and the structure begins:
/search?q=cats & dogs
^---- is "& dogs" a value or a new parameter?
Encoding makes the byte unambiguous. %26 is always “the literal byte 0x26” (& in ASCII), never the parameter separator. So:
/search?q=cats%20%26%20dogs
^---- definitely a literal &, not a separator
The reserved set
RFC 3986 divides characters into three groups:
| Group | Characters | Encoding rule |
|---|---|---|
| Unreserved | A-Z a-z 0-9 - _ . ~ | Never need encoding |
| Reserved | ! * ' ( ) ; : @ & = + $ , / ? # [ ] | Sometimes need encoding (depends on URL component) |
| Other | everything else | Always need encoding |
The unreserved set is fully URL-safe in any component. Encoding them is allowed but unnecessary — a and %61 are equivalent, but a is human-readable.
Component-specific rules
The reserved set is more nuanced than “always encode”: some characters are reserved only in some parts of the URL.
- In a path segment,
/is a separator. To put a literal/inside a path segment value, encode it as%2F. - In a query string,
&and=are separators. To put either inside a value, encode them as%26and%3D. #is always the fragment separator and must be encoded everywhere except as that separator.+in a query string historically means “space” (legacy of HTML form encoding). Some parsers still treat it that way; safer to encode literal+as%2B.- Spaces are always encoded — as
%20in paths and most contexts, sometimes as+in query strings (HTML-form-encoded).
JavaScript provides two encoders that draw the line differently:
| Function | Encodes | Doesn’t encode |
|---|---|---|
encodeURI | spaces, non-ASCII, a few control characters | : / ? # [ ] @ ! $ & ' ( ) * + , ; = (the URL syntax characters) |
encodeURIComponent | everything except unreserved | only the unreserved set |
encodeURI is for encoding a whole URL where the structure characters should remain meaningful. encodeURIComponent is for encoding a value that will be inserted into a component. Almost all real-world use of URL encoding is the latter.
UTF-8 first, then percent
Characters outside ASCII are encoded by first converting the character to UTF-8, then percent-encoding each resulting byte. The Unicode character é (U+00E9) is two bytes in UTF-8 (0xC3 0xA9), which percent-encodes to %C3%A9. The em-dash — (U+2014) is three bytes in UTF-8 (0xE2 0x80 0x94), which encodes to %E2%80%94.
This is why you sometimes see %C3%A9 in URLs and not %E9 — the latter would be a Latin-1 encoding, which most modern web stacks don’t use. URL encoding without UTF-8 conversion produces a sequence that decodes to mojibake on the receiving end.
Common pitfalls
- Double encoding. Calling
encodeURIComponenton a string that’s already percent-encoded turns%20into%2520(%itself becomes%25, then the original20). The receiver decodes once and gets%20instead of a space. If a value passes through multiple URL-aware layers, each layer should encode/decode exactly once. - Encoding the
?separator.https://example.com/search%3Fq=catsis a URL with a path that contains a literal?, not a search URL. The separator must remain unencoded. - Mixing space conventions.
+for space is legal in query strings under HTML form encoding but never in path segments. Decoders should know which convention they’re in. - Assuming safe characters are safe everywhere.
~is unreserved per RFC 3986 but some older systems still encoded it. The cure is worse than the disease — modern parsers handle~literally — so emit unreserved characters as themselves.
For decoding URL-encoded strings, see the URL decoder. For dissecting a full URL into its components, see the URL parser.