By Docify

Punycode vs IDNA: RFC 3492 and UTS #46

• 6 min read

RFC 3492 Punycode encodes Unicode scalar values and does not add xn--. UTS #46 ToASCII (Unicode 18.0.0 / Revision 36, nontransitional) runs NFC, lowercase, and label split, then RFC 3492 with xn-- on each non-ASCII label. Encoding bücher writes bcher-kva on /punycode/. Encoding Bücher.de writes xn--bcher-kva.de on /idna/. Punycode of NFC é writes 9ca; IDNA of that same é writes xn--9ca. Neither page is a URL parser or a DNS lookup.

Punycode is not IDNA. RFC 3492 is a Bootstring encoder over one string. UTS #46 ToASCII is domain processing that uses that encoder after NFC, lowercase, and a label split. This page names those outputs on the live Punycode encoder and IDNA ToASCII tool. It is not new URL() and not a DNS client.

One string versus DNS labels

No xn-- versus xn-- plus a split

Encode on /punycode/ is RFC 3492 Punycode (Bootstring: base 36, tmin 1, tmax 26, skew 38, damp 700, initial_bias 72, initial_n 128). Basic ASCII is copied literally; non-ASCII is a delta after the last hyphen. Encode does not prefix xn--. Decode strips one optional leading xn-- (RFC 3490 / 5890). Encode on /idna/ is UTS #46 ToASCII (Unicode 18.0.0 / Revision 36, nontransitional): trim, NFC, JavaScript toLowerCase, split on U+002E / U+3002 / U+FF0E / U+FF61, then RFC 3492 with xn-- on each non-ASCII label.

RFC 3492 bücher → bcher-kva · UTS #46 Bücher.de → xn--bcher-kva.de

The same host is not interchangeable

Encoding bücher writes bcher-kva on /punycode/. Decode output of that string or xn--bcher-kva is bücher. A dot is a basic code point: bücher.de is bcher.de-65a, not xn--bcher-kva.de. Encoding Bücher.de writes xn--bcher-kva.de on /idna/. Decode output of that ACE is bücher.de. All-ASCII hello is hello- on /punycode/ and stays hello on /idna/. German ß is encoded: Straße → Strae-oqa on /punycode/; Straße.de → xn--strae-oqa.de on /idna/. Ideographic full stop 中文。中国 is xn--fiq228c.xn--fiqs8s on /idna/.

Only IDNA normalizes first

NFC é and NFD e + U+0301

Encode on /punycode/ never calls String.prototype.normalize. Canonically equivalent e + U+0301 and é (U+00E9) are different code-point sequences. Punycode output of NFC é is 9ca. Punycode output of NFD é is e-xbb. Decode of 9ca writes NFC é. Decode of e-xbb writes NFD e + U+0301. Decode of xn--9ca is the same NFC é. Encode on /idna/ runs NFC first, so output of NFC é and of NFD e + U+0301 is the same xn--9ca. Decode output of xn--9ca or mixed-case XN--9CA is NFC é. Try it withbücher or Bücher.de. UAX #15 output e + U+0301 → é (U+00E9) as normalized.txt lives on /unicode-normalize/.

Not a URL parser or a DNS lookup

Host only versus a full URL

Both pages reject a string that starts with http:// or https://. Host output of https://bücher.de/ is hostname xn--bcher-kva.de on /url-parser/. That page applies UTS #46 to the host of a full URL. Percent-encode a string on /url-encoder/. /idna/ is not the full UTS #46 IDNA Mapping Table, not Nameprep, not CheckBidi / CheckJoiners, and not a DNS lookup. Download on both pages is encoded.txt or decoded.txt as text/plain. Encode and Decode are buttons.

Encode the same host with either algorithm

RFC 3492 Punycode or UTS #46 ToASCII in the browser. Nothing is uploaded.

FAQ

What is the difference between Punycode and IDNA?
Punycode encodes Unicode characters. IDNA (Internationalized Domain Names) uses Punycode plus validation rules.
What is the xn-- prefix?
ACE (ASCII Compatible Encoding) prefixes Punycode labels with xn-- to indicate they contain international characters.
Does IDNA normalize Unicode first?
Yes. UTS #46 IDNA runs NFC normalization before encoding, so different character sequences may produce the same result.
Does this tool upload my data?
No. All encoding runs in your browser using JavaScript. Nothing is sent to a server.

Related