explainer
Why Your URL Starts With xn--: Punycode Explained
The xn-- prefix is not a broken link — it is the ASCII form DNS actually carries. How Punycode works, and why browsers show xn-- to block homograph attacks.
You copy a perfectly normal-looking link out of the address bar, paste it into Slack, and what lands is https://xn--mnchen-3ya.de/. Nothing you did caused that, and nothing is corrupted. xn--mnchen-3ya.de is the domain — it is what münchen.de looks like once it has been squeezed into the only alphabet DNS has ever been able to carry. Your browser was rendering the pretty version for you; the moment the text left the address bar, the real hostname came with it.
Here is what the encoding is, how to read one at a glance, and the security reason your browser sometimes refuses to show you the pretty version at all. If you just want the answer for a specific domain, paste it into the Punycode Converter — it converts in both directions and flags the dangerous cases.
DNS only speaks letters, digits and hyphen
The hostname rules are older than Unicode’s adoption on the web: a DNS label is made of ASCII letters, digits and hyphens (the “LDH” set), case-insensitively. That constraint is baked into resolvers, registry software, mail servers and a few decades of middleboxes, and it was never going to change.
So IDNA — Internationalized Domain Names in Applications — takes the opposite approach. Instead of widening DNS, it defines a reversible way to write any Unicode label using only LDH characters, and marks the result with a four-character ACE prefix (ASCII Compatible Encoding): xn--. A resolver sees xn--mnchen-3ya, treats it as an ordinary ASCII label, and never has to know it means anything. Only the software at the edges — your browser, your mail client — decodes it back for display.
The encoding itself is Punycode, specified in RFC 3492 as “a Bootstring encoding of Unicode for Internationalized Domain Names in Applications.” Its shape is easy to read once you know the trick: all the ASCII characters are kept, in order, then a hyphen delimiter, then a compact encoding of which non-ASCII characters go where.
münchen → mnchen-3ya → xn--mnchen-3ya
^^^^^^ ^^^
ASCII the rest, positionally encoded
日本 → wgv71a → xn--wgv71a
(no ASCII at all, so no delimiter hyphen)
That is why xn--mnchen-3ya is legible as “munchen with something on it” and xn--wgv71a is legible as nothing at all — a label with no ASCII letters in it gives you nothing to recognize.
It happens per label, not per domain
This is the part that surprises people reading one for the first time: each dot-separated label is encoded independently, and labels that are already pure ASCII are left unencoded — lowercased, but with no prefix and no transformation.
| Unicode domain | Punycode (ACE) form |
|---|---|
münchen.de | xn--mnchen-3ya.de |
bücher.de | xn--bcher-kva.de |
café.fr | xn--caf-dma.fr |
españa.com | xn--espaa-rta.com |
日本.jp | xn--wgv71a.jp |
한국.kr | xn--3e0b707e.kr |
пример.рф | xn--e1afmkfd.xn--p1ai |
مثال.إختبار | xn--mgbh0fb.xn--kgbechtv |
💩.la | xn--ls8h.la |
пример.рф is the instructive row: both labels are non-ASCII, so both get encoded, and .рф — Russia’s Cyrillic TLD — is permanently xn--p1ai on the wire. Meanwhile .de, .jp and .com pass through untouched. If you are looking at a hostname with one xn-- label and three normal ones, that is not a partial failure; that is correct output.
Two adjacent rules follow from the same per-label logic:
- Email addresses split at the
@. Punycode applies only to the domain.anna@münchen.debecomes[email protected]— the local part is untouched, because a non-ASCII local part is a different problem entirely, solved by SMTPUTF8 (RFC 6531) support on both mail servers rather than by encoding. - Emoji domains exist but are not blessed.
💩.laencodes cleanly and browsers will resolve it, but IDNA2008 classifies emoji as disallowed and the overwhelming majority of registries will not sell you one. A handful of ccTLDs are the exception, not the rule.
Punycode expands, and the 63-octet limit still applies
DNS caps a label at 63 octets and a full domain at 253. Those limits are measured against the encoded form, which is longer — often much longer — than what you typed. 日本語のドメイン名 is nine characters; its ACE form xn--u9jxfoe1dzd408wpd0a0ecl32k is thirty octets. Roughly speaking, CJK inflates about 3×, so a label that looks comfortably short in Japanese can sit right at the DNS ceiling.
This is a real failure mode for anyone generating subdomains programmatically: the length check has to happen after encoding, not before. The converter shows the octet count per label and flags anything over the limit for exactly this reason.
The security half: homograph attacks
Now the reason any of this shows up in your browser instead of staying invisible.
Unicode contains a lot of characters that are drawn identically to Latin letters. Cyrillic а (U+0430) and Latin a (U+0061) are separate code points with separate Punycode encodings and separate DNS records, and in nearly every font on your machine they are the same pixels. So:
| What you see | What it really is | Punycode |
|---|---|---|
аpple.com | first letter is Cyrillic а (U+0430), rest Latin | xn--pple-43d.com |
аррӏе.com | а р р ӏ е — every letter Cyrillic (U+0430 U+0440 U+0440 U+04CF U+0435) | xn--80ak6aa92e.com |
The second row is the well-known demonstration: in April 2017, researcher Xudong Zheng registered xn--80ak6aa92e.com, and Chrome, Firefox and Opera all rendered it in the address bar as apple.com. He obtained a valid Let’s Encrypt certificate for it, so it had a padlock too — which is the point worth internalizing. TLS certifies that you reached the domain in the address bar. It has no opinion about whether that domain is the one you meant.
The attack defeated the defense browsers had relied on since 2005, which was mixed-script detection. аpple.com mixes Cyrillic with Latin and gets caught. аррӏе.com mixes nothing — it is consistently, entirely Cyrillic, and sailed straight through. Chrome shipped whole-script confusable detection in version 58 in response, and Firefox added equivalent rules.
Why your browser shows you xn-- on purpose
Modern browsers treat Unicode display as a privilege a label has to earn. Chrome’s IDN display policy falls back to the raw xn-- form when, among other triggers, a label mixes scripts in a combination it does not allow (Latin with Cyrillic or Greek is out; Latin with CJK is fine), when a label is written entirely in whole-script-confusable letters on a TLD unassociated with that script, when a label’s “skeleton” matches a top domain after diacritics are stripped, or when digits and digit lookalikes are mixed. Firefox applies its own comparable rules, plus an about:config switch (network.IDN_show_punycode) that forces ACE display for everything.
So when a URL suddenly renders as xn--…, that is frequently not a bug report — it is the browser telling you the label failed a safety check.
Two more defenses are worth knowing because they are quiet:
- Your password manager will not autofill on a lookalike. Credentials are keyed to the origin — scheme, host, port — and a homograph domain is a genuinely different host. If the login page looks right and the vault silently declines to fill it, believe the vault, not your eyes. Same logic applies to invisible Unicode characters smuggled into a label: they render as nothing, and they still change the hostname. Look-alike substitution is the same trick that powers “fancy” Unicode text — a letter that looks like yours but isn’t.
- The URL bar is not the only place the real host leaks. Paste the link into the URL Parser and the Host row shows the ACE form, because it runs the browser’s own URL parser. The same is true of anything you paste into a chat client, a terminal, or a bug tracker.
The mapping layer nobody mentions
One wrinkle that trips up people who test this by hand: Punycode is not the only transformation applied to a hostname. Browsers first run UTS #46 mapping, which folds a large set of compatibility characters into their plain equivalents before encoding. The result is that some lookalikes never become separate domains at all:
| What you type | What the browser actually sends |
|---|---|
ⅿicrosoft.com (leading U+217F, small Roman numeral) | microsoft.com |
EXAMPLE.com (fullwidth letters) | example.com |
℡.com (U+2121 telephone sign) | tel.com |
A pure RFC 3492 library gets none of that right — it would dutifully encode ⅿicrosoft to xn--icrosoft-dc6d. Our converter runs the RFC 3492 engine and cross-checks each result against your browser’s URL parser, showing both when they disagree, because the browser’s answer is the one that goes on the wire. Worth remembering the next time you write a domain allowlist: comparing raw input strings is not the same as comparing hostnames. (It is the hostname sibling of the URL encoding problem — normalize before you compare, or you are comparing the wrong thing.)
What to do with a suspicious link
- Convert it. Paste the hostname into the Punycode Converter. It auto-detects direction, encodes or decodes label by label, checks the 63/253-octet limits, and warns on mixed scripts, whole-script confusables and invisible characters, listing the code points of the characters it flags. It runs entirely in your browser.
- Read the
xn--labels, not the pretty ones. A legitimate German or Japanese site has one encoded label and a recognizable TLD. A brand name you expected to be pure ASCII showing up asxn--is the whole warning. - Inspect the characters individually with the Unicode Inspector when you want code points and UTF-8 bytes for a string rather than a domain verdict.
- Check who owns it. Once you have the real ASCII hostname, the WHOIS Lookup gives you the registrar and — usually the giveaway — a creation date from last week. Its DNS records tell a similar story; see DNS record types explained for reading them.
- Stop trusting the padlock as an identity signal. It means the connection is encrypted to the host in the bar. Verifying which host that is remains your job.