A little help goes a long way. Find a tool/

Developer FIELD NOTES · 4 MIN READ

Punycode Explained: How International Domains Fit into ASCII

Why münchen.de is really xn--mnchen-3ya: how Punycode encodes non-ASCII domain labels, what xn-- means, and how to decode it back.

The domain name system is older than the web and stricter than your keyboard: a domain label may contain only letters, digits, and hyphens — ASCII, nothing else. That is why typing münchen.de works even though no resolver anywhere has ever stored an ü. Behind the scenes the label is rewritten as xn--mnchen-3ya, and Punycode is the algorithm doing the rewrite. Understanding it explains international domains, browser address-bar behavior, and a whole family of look-alike tricks.

The problem: DNS predates every accent mark

When DNS was designed, a "label" meant the 26 English letters, ten digits, and the hyphen — the so-called LDH characters. Sixty-three bytes per label, no spaces, no accents, and certainly no Japanese kana. When the internet went global, the choice was stark: replace DNS (impossible) or translate every non-ASCII name into LDH-compatible form on the way in and back on the way out. That translation layer is IDNA, Internationalized Domain Names in Applications, and Punycode is its encoding. The same characters-versus-numbers story underlies all text handling — How Text Encoding Works tells it from the top.

How the encoding squeezes ü into three characters

Punycode works label by label. It keeps the characters that are already legal — for münchen, that is mnchen — deletes the ones that are not, and then appends the deleted information as a compact digit string after a delimiter. The result is mnchen-3ya: mnchen is the ASCII skeleton, the final - announces "encoded characters follow", and 3ya packs two facts into three characters — how many characters were removed (one) and which one (ü, plus where it belongs). The digit alphabet runs a through z for values 0–25 and 0 through 9 for 26–35, which is why the suffix always looks like scrambled lowercase. Full-domain example:

münchen.de        →  xn--mnchen-3ya.de
bücher.example.com →  xn--bcher-kva.example.com
例え.テスト          →  xn--r8jz45g.xn--zckzah

Note what did not change: .de, example.com — anything already ASCII passes through untouched, because per-label encoding means normal domains pay nothing.

Why the prefix is xn--

xn-- is a reserved flag meaning "this label is encoded; decode me before use". Regular labels are not allowed to contain a hyphen in both the third and fourth positions, so xn-- can never collide with a genuine name — the prefix is unambiguous by construction. Your browser decodes it and displays münchen.de in the address bar; the wire, the cache, and the resolver only ever see the ASCII form. Both directions matter in practice: registering an international domain means finding its Punycode form, and reading a certificate, log line, or email header often means decoding it back.

The look-alike problem is built in

Because any Unicode character can hide inside an xn-- label, domains can be crafted where a Cyrillic а stands in for a Latin a, or two labels render identically but decode to entirely different text. Browsers mitigate this — displaying Punycode when a label mixes scripts that look alike — but the underlying ambiguity is real, and it is worth decoding any xn-- label you do not recognize before trusting where it leads. Seeing the decoded form is the difference between "looks like your bank" and "is spelled with three characters your bank would never use".

Punycode is not URL encoding

Two encodings, two layers, endlessly confused. Percent-encoding (the %20 in URLs) encodes characters that are forbidden in a URL path or query string; Punycode encodes characters that are forbidden in a domain label. A path can carry an ü in percent-encoded form — %C3%BC — while the domain must carry it as xn-- punycode, and neither mechanism can substitute for the other. If you are debugging a full URL, both layers can be present at once, in different parts of the same string.

Try it yourself

Type münchen.de into the Punycode Converter and confirm the xn--mnchen-3ya output, then flip to decode mode and paste the result to get the umlaut back. To see the percent-encoding that handles the rest of a URL, try the URL Encoder, and to look up the exact code points behind the non-ASCII characters you are encoding, the Unicode Lookup maps each one to its number.

Frequently asked questions

How is Punycode different from URL encoding?
They solve problems at different layers. Percent-encoding (%20 and friends) handles characters that are forbidden in a URL path or query string; Punycode handles characters that are forbidden in a domain label, which DNS restricts to letters, digits, and hyphens. The same URL can contain both at once: punycode in the domain, percent-encoding in the path.
What does the xn-- prefix mean?
It is a reserved flag meaning 'this label is encoded — decode before use'. Ordinary labels cannot contain a hyphen in both the third and fourth positions, so xn-- can never collide with a real name. Resolvers, caches, and certificates only ever see the ASCII form; your browser decodes it and shows münchen.de in the address bar.
Is the whole domain encoded, or just one label?
Punycode works label by label, split on dots. Only labels containing non-ASCII characters change: münchen becomes xn--mnchen-3ya while .de or example.com pass through untouched. That is why decoding is safe too — an xn-- label is translated back and plain labels are left alone.
Why does münchen become mnchen-3ya?
Punycode keeps the legal characters as a skeleton — mnchen — drops the ones that are not, and appends the removed information after a dash as a compact digit string. The suffix 3ya encodes both how many characters were dropped (one) and which one (ü, with its position). The digit alphabet runs a–z for 0–25 and 0–9 for 26–35, which is why suffixes look like scrambled lowercase.
Can Punycode encode emoji or CJK characters?
Yes — any Unicode code point can be encoded, which is why emoji and Japanese domain names exist. The flip side is spoofing: a label can look identical to a famous one while decoding to different characters, usually via mixed scripts. Browsers show raw punycode for suspicious labels, but decoding an unfamiliar xn-- label before trusting it is cheap insurance.