URL Encoder & Decoder
Every encoding mode developers actually use — encodeURIComponent, encodeURI, form encoding, Punycode IDN — plus a query-string parser. Side-by-side comparison on encode, runs in-browser.
- ComponentencodeURIComponent
Hello%2C%20world!%20%D0%AD%D1%82%D0%BE%20%D1%82%D0%B5%D1%81%D1%82%20https%3A%2F%2Fexample.com%2Fpath%3Fq%3D10%26lang%3Dja - URIencodeURI (path-safe)
Hello,%20world!%20%D0%AD%D1%82%D0%BE%20%D1%82%D0%B5%D1%81%D1%82%20https://example.com/path?q=10&lang=ja - Form+ for spaces
Hello%2C+world!+%D0%AD%D1%82%D0%BE+%D1%82%D0%B5%D1%81%D1%82+https%3A%2F%2Fexample.com%2Fpath%3Fq%3D10%26lang%3Dja - PunycodeIDN xn--
xn--Hello, world! https://example-0uysnykxd6acc.com/path?q=10&lang=ja
Learn More
URLs were designed as ASCII-only strings with strong opinions about which characters carry structural meaning. The slash separates path segments. The question mark introduces the query string. The hash introduces the fragment. The ampersand and equals separate parameter pairs. The colon delimits the scheme. RFC 3986 calls these reserved characters and the encoder's job is to make sure none of them appear inside a slot where they would be misinterpreted. encodeURIComponent assumes nothing about your input — it encodes every reserved character every non-ASCII character every control character. encodeURI assumes you are passing a complete URL and that the reserved characters are doing their structural job — it encodes only non-ASCII characters and a few specific others (% < > etc.) so a fully-formed URL with raw structural characters still works. Form encoding is a historical artefact that refuses to die. application/x-www-form-urlencoded was the original HTML form encoding defined long before URL encoding had a clean spec. The only difference from encodeURIComponent is that spaces become + instead of %20 — a quirk that costs developers many hours of debugging when they round-trip data through HTML forms and find their %20 sequences corrupted. Punycode is the fourth flavor dating from RFC 3492: the encoding that lets DNS represent international domain names. Every browser registry and CA understands xn-- prefixed labels; everything beyond DNS — your application code your logs your URLs displayed in the UI — usually works in Unicode. The translation between them is what Punycode does. This tool runs all four encoders against your input simultaneously so you can see exactly where the differences land and pick the right one for your slot.
Three classes of bugs dominate URL encoding tickets in production. Double-encoding is the most common: code path A encodes the input code path B encodes the already-encoded result and the final URL contains %2520 (which is the encoded form of %20 which is the encoded form of a space). The receiver decodes once and gets %20 back decodes twice and finally gets a space — but only if the receiver knows to decode twice. Most don't and the data path silently corrupts spaces into literal %20 strings. The fix is to encode exactly once and to mark every code path's expected input/output state explicitly. Under-encoding is the next most common: a value containing & = or # gets concatenated into a query string without being encoded and the URL parser at the receiver splits it on the un-escaped delimiter. The user typed AT&T into a search box and the search query becomes?q=AT and the next parameter is named T (because of the unescaped &). The fix is to ALWAYS encodeURIComponent before concatenation — never assume your input is structural-character-free. The third class is alphabet confusion: form-encoded bodies use + for spaces query strings use %20 and decodeURIComponent doesn't understand +. Decoding form-encoded data with decodeURIComponent leaves + signs as literal + characters; decoding URL-encoded data through a form parser changes any literal + into a space. The Decode mode in this tool lets you pick the right alphabet explicitly and shows the decoded result. If the round-trip (encode then decode) doesn't match your input one of the operations is wrong for your context.
Punycode matters whenever a non-ASCII domain crosses the boundary into a system that expects pure ASCII. The Host header in an HTTP request goes over the wire as ASCII bytes — your browser converts the Unicode hostname to Punycode before sending. TLS SNI is the same: the server name is sent in the ClientHello as ASCII Punycode. Certificate Subject Alternative Names list the Punycode form because X.509 was specced before IDN existed. Email systems vary: SMTPUTF8 (RFC 6531) lets the local part be Unicode but the domain part still goes through Punycode for DNS lookup. Browser address bars display Unicode for visual clarity but compare URLs against bookmarks history and HSTS preload lists in Punycode form which is why visiting xn--…-equivalent and the Unicode form give the same security posture. The practical scenarios where you reach for Punycode in code: parsing a hostname out of an arbitrary URL where the scheme is sensible (so URL constructor works) but the hostname has non-ASCII; building a curl command from a URL displayed in a browser; logging a request where you want both the human-readable form and the wire form; looking up a domain in WHOIS which requires the Punycode form; matching a domain against a TLS certificate's SAN list. The tool here splits a hostname on dots encodes only the labels that need it and prefixes those labels with xn--. Decode does the inverse. ASCII-only labels round-trip unchanged.
Frequently asked questions
encodeURIComponent (Component mode) is the right answer 95% of the time. It encodes EVERYTHING that isn't an unreserved URI character (A-Za-z0-9-_.~) including the URL-significant characters like /? # & =: and @. Use it for query parameter VALUES path SEGMENTS fragment contents — any time you are inserting a string into a slot inside a URL. encodeURI is only correct when you have a complete well-formed URL and want to lightly sanitise non-ASCII without breaking the URL structure: it leaves the structural characters (; /?: @ & = + $ #) unencoded. Form mode is encodeURIComponent with %20 swapped for + — historically required by application/x-www-form-urlencoded request bodies still the format most HTML forms use without explicit configuration.
Because URL parsers are greedy and unforgiving. If you put an unencoded? inside a query parameter value every character after it is interpreted as a fragment. If you put an unencoded & inside a value the parser treats it as a parameter separator and your one parameter becomes two. If you put an unencoded # inside a path everything after becomes a fragment dropped before the request even leaves the browser. encodeURIComponent prevents all of these by encoding the structural characters; encodeURI deliberately leaves them unencoded because it assumes you are encoding a whole URL where those characters are doing structural work. Mixing them produces silent data loss bugs that often only surface when a user types something with an ampersand in a form.
Punycode (RFC 3492) is the encoding that lets DNS — which is ASCII-only — represent internationalised domain names like заграница.рф or 香港.中文. The Unicode label gets translated to an ASCII string starting with xn-- (the IDN prefix) which DNS certificate authorities registries and most low-level network code can handle without modification. xn--80aaclnrcbtaceeaa3byde8ho4o.xn--p1ai is what заграница.рф actually looks like on the wire. Browsers display the original Unicode form to humans but use the Punycode form for resolution certificate matching and HSTS lookups. You encounter Punycode whenever you need to put a non-ASCII domain into a Host header a TLS SNI field a DNS query or anywhere else the surrounding system was specced for ASCII-only.
By design — RFC 3986 classifies single quote as an unreserved character (along with A-Z a-z 0-9 - _. ~) so encodeURIComponent leaves it as-is. This trips people up when constructing URLs that get embedded in HTML attributes: the unencoded ' inside an href=... can break out of the attribute. The fix is HTML-escape the URL on the way into HTML not URL-encode it: HTML-escape and URL-encode are complementary operations not alternatives. The same applies to!*'() which encodeURIComponent also passes through — these are technically reserved in some sub-grammars but the spec's Component encoder doesn't know which sub-grammar your output will end up in.
The parser handles three things URLSearchParams doesn't do well in practice. First it accepts a full URL or just the query portion or just the part after the? — URLSearchParams expects a specific subset and silently misparses the rest. Second it preserves duplicate keys as separate rows in the order they appeared which matters for APIs that interpret repeated keys as arrays in declaration order. Third it shows you the raw key and the decoded value side by side so you can spot encoding bugs (a value that is double-encoded shows %25 in the raw form and a literal % in the decoded form). For programmatic use URLSearchParams is fine; for human inspection of an unfamiliar URL the parser saves time.
Yes — and this is one of the most common URL bugs. encodeURIComponent on already-encoded input produces double-encoding (%2520 instead of %20) which is technically valid (the receiver decodes once and sees %20 decodes twice and sees a space). decodeURIComponent on partially-decoded input throws on a stray % that isn't followed by two hex digits. encodeURI on a string that already contains percent escapes produces the same string unchanged which silently masks bugs upstream. Use the Decode tab to verify by round-tripping: encode decode compare. If the input doesn't equal the output of decode(encode(input)) one of those operations is wrong for your context.
More in Data Utilities
Developer validators, formatters and generators for structured data and identifiers.