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ünchenxn--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:

DomainAutoExample
contains a character that is not ASCIIencodesmünchen.dexn--mnchen-3ya.de
is ASCII and has a label that starts with xn--decodesxn--mnchen-3ya.demünchen.de
is plain ASCIIleaves it, in lower caseExample.orgexample.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=ü becomes https://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.de becomes info@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:

  1. 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.
  2. Normalization to NFC, so an é typed as e plus a combining accent gives the same result as a single é.
  3. Checks on each label, for example for invisible joiners and for mixing right-to-left and left-to-right letters.
  4. Punycode for each label that is not ASCII.
InputOutput
MÜNCHEN.DExn--mnchen-3ya.de
EXAMPLE.comexample.com, no Punycode needed
bücher。dexn--bcher-kva.de
straße.dexn--strae-oqa.de
😀.wsxn--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 digits a to z and 0 to 9 may 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--example decodes to Ωίθηδ, with a capital Ω. Browsers write that name in lower case, as xn--kxakid7f, so xn--example is 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:

MessageMeaning
A space is not allowed in a domain name, “*” is not allowed in a domain nameASCII other than letters, digits, -, _ and ..
“%” is not allowed in a domain name; decode a percent-encoded name firstThe 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 nameA 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 addressThe last label consists of digits.
… breaks the IDNA rules (UTS 46) that browsers apply to domain namesA 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 validThe label does not encode back to itself, see decoding.
“xn--mnchen-3ya-” decodes to “mnchen-3ya”, plain ASCII, which is never written as PunycodeA label that did not need Punycode.
“_” in “xn--a_b” is not a Punycode digit, … ends in the middle of a characterThe label is not Punycode, or it was cut off.
The label “…” is 70 characters long in Punycode; DNS allows 63A 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.