Punycode Converter_

Punycode exists so a domain with non-ASCII characters can survive DNS: münchen.de becomes xn--mnchen-3ya.de. That part is mechanical, and every language has a library for it.

The part worth a tool is that it is also the mechanism of the IDN homograph attack. Cyrillic а is not Latin a, and renders identically in almost every font — so аpple.com is a different domain from apple.com, registrable by anyone, and indistinguishable on screen. This converts in both directions and then says which characters are not what they appear to be, and what the name would read as if they were.

toolkit.codes/punycode-converter
Try

Unicode

ASCII · punycode

UTF-8
Ready
100% LOCAL
Input
A domain name in either form. Which direction to go is worked out from the name — a label starting xn-- is decoded, anything with non-ASCII characters is encoded.
Output
Both forms at once, plus every character that renders as a Latin letter without being one, and the name those characters would spell.
Processing
RFC 3492 Bootstring, implemented here and checked against the eleven sample strings printed in the specification. Nothing is looked up.
Limits
It reasons about the name, not the site. It cannot tell you whether a domain is registered, who owns it, or whether a legitimate-looking name is hostile — only whether the characters are what they seem.
Why the top-level domain is never encoded
Punycode is applied per label — each dot-separated part is converted on its own, and one that is already ASCII is left exactly as it is. So münchen.de becomes xn--mnchen-3ya.de and not one encoded blob. Encoding the whole name as a single string produces something DNS cannot resolve.

The same encoding that made IDN possible made phishing easier

One character is enough

Cyrillic а is U+0430 and Latin a is U+0061. They are unrelated characters that happen to be drawn the same way in essentially every typeface. A domain using the first is a different domain from one using the second, so it can be registered by anybody, and no amount of looking at the address bar will separate them. The punycode is where the difference becomes visible — xn--pple-43d.com is unmistakably not apple.com — which is why showing it is the defence rather than a display failure.

Cyrillic is the script that matters

Not because of anything about the language, but because of coverage: Cyrillic contains lookalikes for enough of the Latin alphabet — а е о р с х у і ј ѕ and more — to spell most brand names outright without a single Latin letter. Greek supplies a smaller set. That matters because the main browser defence is noticing a label that mixes scripts, and a name written entirely in one script does not mix anything.

What browsers actually do

Chrome, Firefox and Safari all display the punycode instead of the name when a single label mixes scripts, and increasingly when a label is wholly in a script the user has no reason to read. They do not do it for münchen.de, because nothing there is pretending. If a name you expected to see rendered turns up as xn-- in the address bar, the browser has made a judgement about it, and the judgement is usually right.

Mixed-script detection has a blind spot

A label written entirely in Cyrillic passes the mixed-script test cleanly, because there is only one script in it. That is the whole-script confusable case, and it is the harder one — the 2017 demonstration that registered a working epic.com lookalike used exactly this. Browsers now apply extra rules for it, but the rules are heuristics about which scripts a user is likely to read, and heuristics have edges. This page checks both conditions separately and says which one it found.

The length limit applies after encoding

DNS allows 63 octets per label, and the count is taken on the encoded form. Punycode expands most non-Latin text substantially, so a name that is comfortably short as Unicode can be unregistrable as ASCII — and the failure arrives at the registry rather than at the keyboard. This shows the encoded length for every label, which is the number that actually has to fit.

Encoding is reversible; the mapping before it is not

Bootstring itself is exactly reversible. What happens before it is not: IDNA lowercases, normalises, and rejects some characters outright, so MÜNCHEN.DE and münchen.de produce the same ASCII and only one of them comes back. This is correct — DNS is case-insensitive and a domain has one canonical form — but it means round-tripping a name through both directions can return something that is not character-for-character what you started with.

Paste a name in either form

  1. 01Paste the domain. Which direction to convert is worked out from the name itself — no toggle to get wrong.
  2. 02Read both forms side by side. If they differ more than you expected, that is the finding.
  3. 03Check the verdict. It says whether any character is impersonating a Latin letter, and what the name would spell if it were read as those letters.
  4. 04Take the punycode when you are recording a suspicious domain. It is unambiguous in a ticket, a blocklist or an email in a way the rendered name is not.
  5. 05Try the samples if you want to see both attack shapes — one Cyrillic letter in a Latin word, and a word written entirely in Cyrillic.

A link that looks right

It arrived in an email and the address bar shows the brand name you expected. One letter is not from the alphabet you assume.

What you see
аpple.com
What it is
xn--pple-43d.com

U+0430 Cyrillic а.
Reads as "apple.com".

An address bar showing xn--

A site you use renders as punycode and looks broken. It is not broken — the browser has decided the name is confusable.

In the bar
xn--80ak6aa92e.com
Decoded
аррӏе.com

Every letter Cyrillic.
The browser is protecting you.

Registering an internationalised domain

The Unicode name is short and the encoded form is not. DNS counts the encoded octets, and the registry will refuse.

Looks fine
17 characters of Unicode
Does not fit
Over 63 octets encoded.
The limit applies after
punycode, not before.

Recording a domain in a ticket

Pasting the rendered name into a report loses the very thing that made it dangerous, because it renders the same for the next reader too.

Ambiguous
аpple.com
Unambiguous
xn--pple-43d.com

Always record the ASCII form.

What punycode does to a name

InputEncodedNote
münchen.dexn--mnchen-3ya.deOne label encoded, the TLD untouched
münchen.köln.dexn--mnchen-3ya.xn--kln-sna.deEach label converted on its own
日本語.jpxn--wgv71a119e.jpA whole label in one script — what IDN is for
example.comexample.comAlready ASCII, so nothing happens at all
MÜNCHEN.DExn--mnchen-3ya.deIDNA lowercases first — the case does not survive
аpple.comxn--pple-43d.comOne Cyrillic letter. A different domain entirely
ѕсоре.comxn--e1afmkfd.comEvery letter Cyrillic. Mixed-script checks miss this

The xn-- prefix is fixed by RFC 5890 and is how anything knows a label is encoded at all. A label that does not start with it is passed through unchanged, which is why an ASCII domain is never touched.

Working with internationalised domains

  • Record the punycode, never the rendered name. In a ticket, a blocklist or an incident report the ASCII form is the only unambiguous one.
  • Treat an unexpected xn-- in the address bar as information. The browser showed it because something about the name met a confusability rule.
  • Do not read a legitimate IDN as suspicious. münchen.de and 日本語.jp are ordinary names, and nothing in them is impersonating anything.
  • Check the encoded length before registering. The 63-octet limit applies after conversion, and non-Latin text expands a long way.
  • Compare skeletons rather than names when deduplicating a domain list. Two names that reduce to the same Latin letters are worth looking at together.
  • Expect case and normalisation to be lost. IDNA folds both before encoding, so a round trip returns the canonical form rather than what you typed.

Where internationalised domains go wrong

One substituted character is invisible

Cyrillic а and Latin a are drawn identically in almost every font, and no amount of care in reading an address bar will separate them. The only reliable comparison is of the encoded form.

Mixed-script detection does not catch a whole-script name

A label written entirely in Cyrillic contains no script mixture to detect. Browsers apply further heuristics about which scripts a user reads, and heuristics have edges.

A punycode address bar is not a broken site

It is a deliberate display decision. Treating it as a rendering bug and proceeding anyway is exactly the outcome the attack depends on.

The length limit bites after encoding

Non-Latin text expands substantially, so a short name can exceed the 63-octet DNS limit once converted, and the failure appears at registration time.

Certificates do not prevent it

A homograph domain can hold a perfectly valid certificate for itself, because it really is the domain named in it. The padlock says the connection is to that name, not that the name is the one you meant.

Bootstring, IDNA and confusables

Algorithm
RFC 3492 Bootstring with the punycode parameters: base 36, tmin 1, tmax 26, skew 38, damp 700, initial bias 72, initial n 128. Implemented here and checked against the eleven sample strings in §7.1.
Prefix
xn--, the ASCII-Compatible Encoding prefix fixed by RFC 5890. A label without it is not punycode and is passed through.
Scope
Per label. Each dot-separated part is converted independently and an ASCII label is untouched, which is why the top-level domain survives.
Mapping
IDNA lowercases before encoding, so case does not round-trip. Bootstring itself is exactly reversible; the mapping in front of it is not.
Length
63 octets per label, counted on the encoded form. Non-Latin text expands, so the Unicode length is not the one that matters.
Confusables
A curated set — the Cyrillic and Greek characters that render as Latin letters, which is what appears in real registrations. Latin letters that resemble other Latin letters are deliberately excluded, since they are a different problem and invisible to script-based defences.
Network
None from tool code. A test sweep calls every function this page uses with fetch and XMLHttpRequest replaced by stubs that throw, so a stray request fails the build instead of shipping. Disconnect from the network and the page still works.

Questions about punycode and IDN

What is punycode?

An encoding that represents Unicode domain labels in ASCII, so they can pass through DNS. münchen.de becomes xn--mnchen-3ya.de. It is defined by RFC 3492 and applied one label at a time, which is why the top-level domain is left alone.

What does xn-- mean at the start of a domain?

It is the ASCII-Compatible Encoding prefix, fixed by RFC 5890, and it marks the label as punycode. Anything after it decodes back to the Unicode the label really contains. A label without the prefix is ordinary ASCII and is not encoded at all.

Why does my browser show xn-- instead of the domain name?

Because it decided the name is confusable — usually because one label mixes scripts, or is written entirely in a script you have no configured reason to read. It is a deliberate protection, not a rendering fault, and the right response is to look at what the punycode decodes to rather than to ignore it.

What is an IDN homograph attack?

Registering a domain that renders identically to a well-known one by substituting characters from another script. Cyrillic а for Latin a is the classic: аpple.com is a different domain from apple.com, available to anyone, and indistinguishable on screen. The punycode is where the difference becomes visible.

How do I tell whether a domain is a homograph?

Convert it. If the ASCII form contains xn-- where you expected plain letters, something in the name is not what it looks like. This page also names the individual characters and shows what the name would spell if they were the Latin letters they resemble.

Is punycode a security risk?

The encoding is not — it is a transport mechanism, and showing it is how browsers defend against confusable names. The risk is in what internationalised domains allow, and punycode is the tool that makes it visible rather than the cause.

Why is my internationalised domain rejected as too long?

Because the 63-octet DNS limit applies to the encoded label, and punycode expands non-Latin text substantially. A name that is fifteen characters of Unicode can exceed the limit once converted, which is why the encoded length is worth checking before you register anything.

Does converting to punycode lose anything?

The Bootstring transformation is exactly reversible. The IDNA mapping applied first is not: it lowercases and normalises, so MÜNCHEN.DE and münchen.de encode identically and only the canonical form comes back. That is correct behaviour, since DNS is case-insensitive.

Why is the Node.js punycode module deprecated?

Because the platform now has WHATWG URL parsing and Intl, which handle IDNA properly, while the bundled module implemented only the Bootstring layer without the mapping around it. The algorithm is not deprecated — the built-in module is. Use the URL constructor, or a userland IDNA implementation.

Is the domain I paste here looked up anywhere?

No. The work is JavaScript running in this tab. Every function it calls is covered by a test that stubs fetch and XMLHttpRequest to throw, so a request that slipped in would break the build rather than reach a server — and you can confirm it for yourself by disconnecting and carrying on.