URL Encode / Decode
Percent-encode or decode URLs and URL components instantly. Pick Component (a query value or path piece) or Full URI (a whole URL). Runs entirely in your browser. Last reviewed 2026-06-19.
How URL (percent) encoding works
A URL may only contain a limited set of characters. Anything outside that set — spaces,
most punctuation, and non-ASCII text — is replaced by % followed by the
character's UTF-8 byte values in hexadecimal. A space becomes %20, an ampersand
%26, and é becomes %C3%A9.
Component vs. full URI
This is the distinction most tools blur. Component mode
(encodeURIComponent) encodes the reserved structural characters too
(: / ? # [ ] @ & = +), so it is correct for a single query-string value or path
segment. Full-URI mode (encodeURI) leaves those structural
characters intact and only fixes spaces and illegal characters, so it is for encoding a whole
URL at once. Using the wrong one is a common source of broken links.
Reserved vs. unreserved characters
Per RFC 3986, the unreserved characters A–Z a–z 0–9 - _ . ~ are
never encoded. Reserved characters have a structural meaning in a URL and are
encoded only when they appear as data rather than as delimiters.
The complete RFC 3986 character sets
The modern rulebook for URLs is RFC 3986, published in January 2005. It obsoletes the older RFC 2396 (and RFC 1808 and RFC 2732) and updates the original URL specification RFC 1738. RFC 3986 divides the ASCII characters a URI may use into three groups, and getting them right is the whole game.
| Group | Characters | Role |
|---|---|---|
| Unreserved | A–Z a–z 0–9 - _ . ~ | Always safe; never need encoding and should not be encoded unnecessarily. |
| Reserved — gen-delims | : / ? # [ ] @ | Major structural separators between scheme, authority, path, query and fragment. |
| Reserved — sub-delims | ! $ & ' ( ) * + , ; = | Sub-component delimiters, for example the dividers between query parameters. |
Reserved characters carry meaning as delimiters. A URI that differs from another
only in the percent-encoding of an unreserved character is considered
equivalent, so encoding A as %41 is legal but pointless and
well-behaved software normalizes it back. Reserved characters, by contrast, must be
percent-encoded whenever they appear as literal data rather than as a separator:
an ampersand inside a search term has to become %26 so it is not mistaken for
the divider between two query parameters.
How a character becomes a percent-encoded octet
Percent-encoding works on bytes, not on characters directly. RFC 3986 defines the
triplet grammar pct-encoded = "%" HEXDIG HEXDIG: a literal percent sign
followed by two hexadecimal digits that spell out one 8-bit octet. The space character
(US-ASCII value 32, binary 00100000, hex 20) therefore encodes to
%20.
For anything beyond plain ASCII the rule is unambiguous in modern URLs: encode the
text as UTF-8 first, then percent-encode each resulting byte that is not in the
unreserved set. Because most non-ASCII characters occupy two, three or four UTF-8 bytes,
they expand into several triplets. The accented é becomes %C3%A9;
the Japanese katakana letter a (ア) becomes %E3%82%A2; a four-byte
character such as an emoji expands to four triplets. The JavaScript built-ins this tool
uses already follow UTF-8, which is why round-tripping accents, CJK text and emoji works
correctly.
encodeURI, encodeURIComponent and the deprecated escape
JavaScript ships three encoders, and confusing them is a classic bug. The table below lists exactly which characters each one leaves un-encoded — everything else becomes a percent-triplet.
| Function | Leaves unencoded | Use it for |
|---|---|---|
encodeURIComponent | A–Z a–z 0–9 - _ . ! ~ * ' ( ) | A single value: one query parameter, one path segment or a form field. |
encodeURI | The above plus ; / ? : @ & = + $ , # | A whole, already-structured URL where the separators must survive. |
escape (deprecated) | A–Z a–z 0–9 @ * _ + - . / | Nothing — avoid it entirely. |
The eleven punctuation marks - _ . ! ~ * ' ( ) that even
encodeURIComponent leaves alone are the RFC 3986 unreserved marks together with
a few legacy characters. Everything structural — : / ? # [ ] @ and the crucial
&, = and + — is encoded, which is exactly why
encodeURIComponent is the right choice for an individual value. The legacy
escape function is deprecated and non-standard: it does not use UTF-8 (it emits
%XX for code points below 256 and a non-standard %uXXXX form above
that), so a character like ć wrongly becomes %u0107 instead of its
UTF-8 bytes. Never use it for URLs.
The space problem: %20 versus +
One byte causes more confusion than any other: the space. In a generic URI per RFC 3986 a
space is %20. But HTML forms submitted as
application/x-www-form-urlencoded follow a different, older convention — defined
today in the WHATWG URL Standard — where a space becomes a literal plus sign +,
and a literal plus is therefore encoded as %2B to keep the two apart. The
browser's URLSearchParams API uses this form encoding, so it serializes a space
as +, whereas encodeURIComponent produces %20.
Both are valid in the query string of a real URL, and a correct decoder treats +
as a space only in form-encoded data — never in a path. Decoding + to a
space inside a path segment is a common bug that corrupts file names and slugs. If you paste
form data here and want plus signs treated as spaces, replace them before decoding; for
ordinary URLs, leave them as they are.
Encoding depends on the part of the URL
A URL is not one flat string; it is a sequence of components — scheme, userinfo, host, port, path, query and fragment — and each has its own set of legal characters. The practical consequences:
- Path segments may contain sub-delims and
: @, but a literal/inside a single segment must be written%2F, or it is read as a new segment boundary. - Query strings treat
&and=as separators, so any value containing them must be percent-encoded;?and/, however, are allowed unescaped inside a query. - Fragments (after
#) are never sent to the server — the browser handles them — but still follow percent-encoding for non-ASCII content. - Userinfo (the rarely-used
user:password@before the host) must encode@and:when they appear as data rather than as the separators.
This is why a single "encode the whole thing" pass is wrong: encode each component with
encodeURIComponent first, then join them with the literal structural characters
you actually intend.
International domains and IRIs: Punycode, not percent-encoding
Percent-encoding handles non-ASCII text in the path, query and
fragment, but it cannot be used in the host name — DNS accepts only
a restricted ASCII alphabet. Internationalized domain names are instead converted with
Punycode (RFC 3492) under the IDNA standard. Each non-ASCII label is
transformed and given the ASCII-compatible prefix xn--: münchen.de
becomes xn--mnchen-3ya.de, and the label bücher becomes
xn--bcher-kva. Browsers display the friendly Unicode form but send the
xn-- form on the wire.
The broader concept is the IRI (Internationalized Resource Identifier,
RFC 3987), which permits Unicode throughout. An IRI is mapped to a plain URI by normalizing
the text (Unicode NFC), UTF-8-encoding the non-ASCII characters in the path and query, and
percent-encoding those bytes — while the host is converted with Punycode. So
https://en.wiktionary.org/wiki/Ῥόδος becomes
https://en.wiktionary.org/wiki/%E1%BF%AC%CF%8C%CE%B4%CE%BF%CF%82. In short: the
host uses Punycode, everything else uses percent-encoding.
Double-encoding: the bug that adds a layer too many
Double-encoding happens when already-encoded text is encoded again. The
percent sign is itself a reserved character, so encoding %20 a second time
yields %2520 — because the leading % becomes %25. The
reverse mistake, decoding once when two layers exist, leaves stray %xx sequences
in the output. Symptoms include URLs that show %2520 in the address bar, or file
names that arrive with a literal %20 baked into them.
The fix is to be deliberate about how many times a value is encoded as it passes through layers — your code, an HTTP library, a proxy, a framework's router — and to decode exactly that many times. Encode once, at the moment you build the URL; never run a value through an encoder twice "to be safe." To check a suspicious value, decode it here repeatedly: if a second decode changes the result, it was double-encoded.
Security: where naive encoding and decoding go wrong
URL encoding is a frequent ingredient in web vulnerabilities, almost always because a decoded value is trusted without re-validation.
- Open redirects. A
?next=or?return_to=parameter that is decoded and used as a redirect target lets an attacker send users to a hostile site. Validate the decoded destination against an allow-list of hosts — never redirect to an arbitrary decoded URL. - Cross-site scripting (XSS). Decoding turns
%3Cscript%3Eback into a live tag. If that decoded text is written into a page without HTML-escaping it can execute. Percent-decoding and HTML-escaping are different jobs: do the second before the value reaches the DOM. - Filter and WAF bypass. Attackers encode and double-encode payloads —
..%2Ffor../, or%2527for a quote — to slip past a filter that only inspects the raw form. Normalize input to a single canonical decoded form before applying security checks, not after. - Log and header injection. A decoded newline (
%0A) or carriage return (%0D) injected into a value that ends up in a log line or HTTP header can forge entries or split a response. Strip control characters after decoding.
The throughline: percent-encoding is a transport convenience, not a security boundary. Always validate and contextually escape the decoded value for wherever it is finally used.
Beyond web links: other places percent-encoding appears
Percent-encoding is not limited to the https:// addresses in your browser bar;
the same RFC 3986 rules govern many other identifiers, and the per-context discipline above
applies to each.
- mailto: links. A pre-filled email link such as
mailto:hi@example.com?subject=Hello%20there&body=Line%20oneuses percent-encoding for spaces and punctuation in the subject and body, and an ampersand to separate the fields — so any literal ampersand in the body text must itself be%26. - data: URIs. Inline resources like
data:text/plain,Hello%20Worldpercent-encode their payload (or carry it as Base64 instead), which is why this tool pairs naturally with a Base64 encoder. - OAuth and redirect parameters. An OAuth
redirect_uriis a whole URL nested inside another URL's query string, so it must be encoded as a single component withencodeURIComponent— a textbook case where forgetting to encode (or double-encoding) silently breaks the login flow. - HTTP authentication and APIs. Credentials placed in the userinfo component, signed-request signatures and many REST path parameters all rely on consistent, single-pass percent-encoding so that the value the server reconstructs matches the value you sent.
In each of these settings the failure mode is identical to a broken web link: a reserved character that was meant as data slips through as a delimiter, or an encoded value is decoded one time too few or too many. Encode each value once, for the exact slot it occupies, and the identifier round-trips cleanly everywhere.
Frequently asked questions
- What is URL encoding?
- URL (percent) encoding replaces characters that are unsafe or reserved in a URL with a "%" followed by their hexadecimal byte value — for example a space becomes %20 and an ampersand becomes %26. It lets arbitrary text travel safely inside a URL.
- Component mode vs full-URI mode — which do I use?
- Use "Component" (encodeURIComponent) when encoding a single piece that goes inside a URL — a query-string value, a path segment or a form field — because it also encodes :/?#[]@&=+ etc. Use "Full URI" (encodeURI) when you have a whole URL and only want to fix spaces and illegal characters while leaving the structural characters intact.
- Does it handle Unicode and emoji?
- Yes. Encoding is done over UTF-8, so accented letters, non-Latin scripts and emoji percent-encode and decode back correctly.
- Is anything sent to a server?
- No. Encoding and decoding run entirely in your browser, so your data stays on your device. The tool also works offline once loaded.