Punycode Converter
Convert internationalized domain names to Punycode and back, in plain domains, URLs and e-mail addresses, with the normalization browsers use and a reason for every name that cannot be converted.
Overview
DNS only carries ASCII letters, digits and the hyphen. Internationalized domain names (IDNA) write every
label with other characters in ASCII: xn-- followed by the label's Punycode, the encoding defined
in RFC 3492. In münchen → xn--mnchen-3ya, the ASCII letters of the label come first,
and after the last hyphen a compact code says which character goes where, here the ü.
Browsers show the Unicode form in the address bar and use the ASCII form for everything else: DNS lookups,
certificates, the Host header. This tool converts in both directions.
Web interface
Type or paste a name into the input, one per line; the output updates as you type. Auto works out the direction for each line, Encode and Decode fix it for all of them. The dice in the corner of the input fills in one of nine examples, Copy copies the output, Clear empties everything and returns to Auto. Ctrl/⌘ + Enter runs the conversion again.
The hint above the input gives the number of names and, in Auto, the direction. The hint above the output gives the number of labels converted, or of warnings and errors, and the line under the output says what happened. With more than one line, each error and warning names its line; the first three are shown.
How Auto decides
Auto looks at the domain of each line, not at a path or an e-mail user name around it:
| Domain | Auto | Example |
|---|---|---|
| contains a character that is not ASCII | encodes | münchen.de → xn--mnchen-3ya.de |
is ASCII and has a label that starts with xn-- | decodes | xn--mnchen-3ya.de → münchen.de |
| is plain ASCII | leaves it, in lower case | Example.org → example.org |
Lines are decided one by one, so a list can mix both directions.
What the input may be
- a domain name, with or without a trailing dot:
münchen.de - a URL:
https://münchen.de:8080/pfad?q=übecomeshttps://xn--mnchen-3ya.de:8080/pfad?q=ü. Scheme, user name, port, path, query and fragment stay as they are; to encode those, use the URL Encoder. - a domain with a path but no scheme:
münchen.de/pfad - an e-mail address:
info@straße.debecomesinfo@xn--strae-oqa.de. The part before the@stays; it has no Punycode form, and a user name with other characters than ASCII needs mail servers that support it (RFC 6531).
An IPv6 address in brackets is left alone. Spaces at the start and end of a line are ignored, empty lines are kept, and a line with an error stays empty in the output, so the output lines up with the input line by line.
How encoding works
Encoding is done by the browser's own URL parser, the one that handles every address typed into the address bar. It applies UTS 46, the Unicode rules for domain names:
- Mapping: capitals become lower case, full-width letters become normal ones,
。,.and。become dots, and a few invisible characters such as the soft hyphen are removed. - Normalization to NFC, so an
étyped aseplus a combining accent gives the same result as a singleé. - Checks on each label, for example for invisible joiners and for mixing right-to-left and left-to-right letters.
- Punycode for each label that is not ASCII.
| Input | Output |
|---|---|
MÜNCHEN.DE | xn--mnchen-3ya.de |
EXAMPLE.com | example.com, no Punycode needed |
bücher。de | xn--bcher-kva.de |
straße.de | xn--strae-oqa.de |
😀.ws | xn--e28h.ws |
The ß stays a ß. The older transitional processing turned it into
ss, so straße.de and strasse.de were the same name; current browsers
keep them apart. Emoji are accepted by UTS 46 and by browsers, but IDNA2008, the rules registries follow, does
not allow them, so only a few top-level domains such as .ws register them.
Chrome 152, Safari's engine WebKit 26.5 and Firefox 153 give the same result for every valid name tested.
They differ on ASCII punctuation, which Chrome percent-encodes and Firefox refuses, and on invalid
xn-- labels, which Chrome and Safari let through. The tool checks both itself before the browser
sees the name, so the result is the same in every browser: ASCII in a domain may be letters, digits, the
hyphen, the underscore (as in _dmarc) and the dot, and every xn-- label in the input
has to be valid.
DNS allows at most 63 characters per label and 253 per name, counted in the ASCII form. A longer name is still converted, with a warning.
How decoding works
Browsers offer no way to turn Punycode back into Unicode, so decoding is RFC 3492 on the page. It works on
whole characters, so an emoji is decoded as one. Every xn-- label is checked:
- something has to follow
xn--, and after the last hyphen only the Punycode digitsatozand0to9may appear; - it has to decode to at least one character that is not ASCII, since a plain ASCII label is never written as Punycode;
- what it decodes to has to encode back to exactly the same label.
xn--exampledecodes toΩίθηδ, with a capitalΩ. Browsers write that name in lower case, asxn--kxakid7f, soxn--exampleis refused.
Labels without xn-- are put in lower case, and labels that are already Unicode stay as they
are.
Errors and warnings
A name that cannot be converted is not converted half-way. Its line stays empty in the output and the line under the output says why:
| Message | Meaning |
|---|---|
A space is not allowed in a domain name, “*” is not allowed in a domain name | ASCII other than letters, digits, -, _ and .. |
“%” is not allowed in a domain name; decode a percent-encoded name first | The name is percent-encoded, as copied from some URLs. Decode it first, for example in the URL Encoder. |
“” (U+200D) is not allowed in a domain name | A character UTS 46 does not allow here, named with its code point because it may be invisible, like this zero width joiner. |
“münchen.123” ends in a number, which makes browsers read it as an IPv4 address | The last label consists of digits. |
… breaks the IDNA rules (UTS 46) that browsers apply to domain names | A rule about whole labels, most often the one against mixing right-to-left and left-to-right letters in one label. |
“xn--example” decodes to …, which browsers write as …, so this form is not valid | The label does not encode back to itself, see decoding. |
“xn--mnchen-3ya-” decodes to “mnchen-3ya”, plain ASCII, which is never written as Punycode | A label that did not need Punycode. |
“_” in “xn--a_b” is not a Punycode digit, … ends in the middle of a character | The label is not Punycode, or it was cut off. |
The label “…” is 70 characters long in Punycode; DNS allows 63 | A warning: the name is converted, but DNS cannot hold it. |
No API
There is no API for this tool. The conversion runs in the browser; in a script, use a library of the
language at hand, such as url.domainToASCII() and url.domainToUnicode() in
Node.js or the idna package in Python.
Limits
The input has no length limit. 10,000 names convert in under 0.1 seconds in every browser tested, drawing included, in either direction.
A single Punycode label longer than 1,000 characters is not decoded. DNS allows 63, so such a label is no domain name, and decoding it would take longer and longer as it grows.
Privacy
Nothing leaves the browser. The conversion runs on the page, no DNS lookup is made, so the tool does not tell whether a domain exists, nothing is stored, and Copy writes to the clipboard only when you press it.