Punycode and IDN Converter Internationalised Domain Names

Punycode converter — Chunky Munster

Punycode is the glue between human-friendly international domain names and the ASCII-only world DNS still speaks. If you need to check how a domain will be stored, compared, or resolved, give our free Punycode converter a spin and see both directions side by side.

This matters any time a hostname contains characters outside plain English letters, numbers, and hyphens. The browser may show one thing, the wire may carry another, and the mismatch is where bugs, phishing tricks, and bad assumptions like to hide.

What Punycode actually is

DNS labels are still limited by legacy rules, so a domain like münich.example cannot be sent to the DNS resolver exactly as typed. The internationalized form, or IDN, is displayed in Unicode for humans, then converted into an ASCII-compatible label for DNS lookup.

Punycode is the encoding scheme used for that conversion. The encoded label gets an xn-- prefix, which tells systems, browsers, and tooling that the rest of the string is an ASCII representation of Unicode text.

That is the key detail: Punycode does not rename the domain. It stores the same label in a format DNS can handle. If you decode it, you should get back the original Unicode string, not some “close enough” transliteration.

Why developers keep running into it

If you build anything that parses or displays hostnames, Punycode will show up eventually. Common examples include signup forms, redirect handlers, allowlists, email validation, certificate checks, and browser automation scripts.

It also matters when two strings look identical but are not. Unicode can represent visually similar characters from different scripts, which is useful for real international domains and also a neat little trap for phishing and spoofing.

That is why security-sensitive code often normalises or canonicalises hostnames before comparison. A domain should be checked in the exact form your infrastructure expects, not just whatever the user pasted into a text box.

If you want a broader refresher on the moving parts behind hostnames, our guide on how Punycode and IDNs work walks through the underlying rules without the hand-waving.

How the encoding works at a practical level

Punycode takes a Unicode label, preserves the ASCII characters already present, and encodes the non-ASCII part into a compact ASCII payload. The algorithm is designed to be reversible, so decoding gets you back to the exact Unicode label.

For example, the label münich becomes xn--mnich-kva. The ASCII letters mnich stay readable, while the accented character is folded into the encoded section.

For developers, the important part is not memorising the algorithm. It is knowing when a string is already encoded, when it still needs conversion, and whether you should compare the Unicode form or the ASCII form in your code path.

Unicode label:  münich.example.com
Punycode form:  xn--mnich-kva.example.com

Unicode label:  bücher.example
Punycode form:  xn--bcher-kva.example

Remember that Punycode is applied label by label. Only the parts between dots are encoded, not the whole domain as one blob.

Where it helps in debugging

One of the most useful checks is to see whether a domain is stored in Unicode, Punycode, or both at different stages of your stack. A reverse proxy might preserve the Unicode hostname, a backend might normalise to ASCII, and a log file might show whichever form the library happened to emit.

That gets messy fast when you are tracing redirects or certificate issues. If a user reports that a domain works in the browser but fails in your app, compare the host exactly as your URL parser sees it, not just as it appears in the address bar.

It also helps when you are validating allowlists. If your rule says example.com, that does not automatically cover xn--example-... variants or similar-looking Unicode labels. Decode first, then compare on a consistent representation.

Browser display, DNS lookup, and security gotchas

Browsers try to make IDNs readable, which is good for users and sometimes annoying for engineers. They may display the Unicode version while still resolving the Punycode version behind the scenes.

That split is where spoofing risk lives. A malicious domain can use lookalike characters from another script, making it visually similar to a trusted site. Modern browsers try to reduce obvious abuse, but you should still treat visually similar hostnames with suspicion in security workflows.

There is also the plain old software compatibility problem. Some libraries, APIs, and older systems still expect ASCII-only hostnames, so feeding them raw Unicode can fail in subtle ways. If a tool touches DNS, certificates, or hostname parsing, test both directions.

A Worked Example

Suppose a user gives you this domain in a support ticket:

https://münich.example/path?q=1

The path and query are not the issue here. The hostname is the part that needs IDN handling.

If you isolate the host and convert it, you get:

Input host:    münich.example
Encoded host:  xn--mnich-kva.example

Now imagine the reverse. You receive this from a DNS record, log, or config file:

xn--mnich-kva.example

Decoding it should restore the human-readable version:

Decoded host:  münich.example

That round trip is what makes a good Punycode converter useful. If the output changes more than expected, you know the label may contain an error, be malformed, or belong to a different script than the one you thought you were handling.

Using Punycode in your own code

Most modern languages and URL libraries can handle IDNs, but the exact behaviour varies. Some normalise automatically, some expose both forms, and some leave the job to you.

A common pattern is to convert hostnames at the boundary: encode before DNS-facing operations, decode before display, and keep one canonical form for comparison. That keeps your application from mixing Unicode input with ASCII-only infrastructure in the same branch of logic.

// Pseudocode
host = parseHostname(url)
asciiHost = idnToAscii(host)
lookupDns(asciiHost)

// later, for display
displayHost = idnToUnicode(asciiHost)

If you are using a URL parser, check whether it already performs IDN handling. Double-encoding a label or decoding something that was never encoded are both easy ways to create bugs that look like “DNS is broken” but are really input-handling mistakes.

For batch workflows, this can save time too. If you are moving between hostname lists, config files, or test fixtures, pair the converter with a text workflow and keep the encoded and decoded forms visible while you sanity-check each line.

Frequently Asked Questions

What is the difference between IDN and Punycode?

IDN means Internationalized Domain Name: a domain label written in Unicode for humans. Punycode is the ASCII encoding used to represent that label in DNS. In practice, IDN is the readable form and Punycode is the transport form.

Why do Punycode domains start with xn--?

The xn-- prefix marks a label as encoded Punycode. It lets DNS-aware systems know the rest of the label should be decoded back to Unicode. If you see that prefix, you are looking at the ASCII form of an internationalized label.

Can two different Unicode domains look the same after encoding?

They should not collapse into the same Punycode output if they are truly different labels, but visually similar Unicode characters can still be confusing to humans. That is why lookalike scripts are a security concern. The encoding is reversible, but the rendered text can still fool the eye.

Should I store domain names in Unicode or Punycode?

Store whichever form is easiest to compare reliably in your system, but be consistent. Many applications keep a canonical ASCII/Punycode form for transport and validation, then render Unicode for display when appropriate. The important part is not mixing forms without a clear rule.

Wrapping Up

Punycode is the boring-looking part of a very non-boring problem: getting international domain names through an ASCII-only protocol without mangling them. If you deal with URLs, DNS, certificates, or hostname validation, it is worth knowing how the Unicode and encoded forms map to each other.

The quickest way to stay sane is to check both representations whenever a hostname looks odd, fails validation, or behaves differently in the browser than in your backend. Use the right form for the right layer, and do not trust your eyes alone when the string contains non-ASCII characters.

When you need to encode, decode, or sanity-check a hostname, use the Punycode converter and keep the round trip visible. It is a small tool, but it saves a lot of time when the domain name starts acting like a gremlin.

// try the tool
give our free Punycode converter a spin →
// related reading
← all posts