Back to Character Encoding

Punycode Punycode (IDN Domain Encoding) Encoding Schemes

The encoding that lets internationalized domain names (like 中国.com) work within DNS's ASCII-only constraints, producing the recognizable 'xn--' prefix.

What it is

Punycode is a specialized encoding that converts internationalized domain names (IDNs) — domains containing non-ASCII characters, like 中国.com or café.com — into an ASCII-only representation that DNS (which only supports ASCII) can actually store and resolve. Punycode-encoded domain labels are always prefixed with xn--, which is why you'll sometimes see that prefix in browser address bars, raw URL data, or server logs when an internationalized domain is involved.

This is a fundamentally different mechanism from percent-encoding (used for URL paths/queries) — Punycode specifically addresses the domain name portion of a URL, which has its own separate ASCII-only constraint rooted in DNS's original design.

How it works

Step What happens
Input A domain label containing non-ASCII characters, e.g. café
ASCII characters extracted Any ASCII characters in the label are kept as-is, moved to the front
Non-ASCII characters encoded Remaining non-ASCII characters are converted into a specially designed ASCII-safe encoding appended after a - separator
Prefix added The whole result is prefixed with xn-- to signal "this label is Punycode-encoded" to anything processing it
Output café.com becomes xn--caf-dma.com

The actual encoding algorithm (a variable-length integer encoding scheme, specified in RFC 3492) is more mathematically involved than a simple substitution — but the practical result is always: ASCII-only output, xn-- prefix, and full reversibility back to the original Unicode domain label.

Why your domain looks wrong (symptom → cause)

Symptom Likely cause
A URL shows xn-- followed by seemingly random characters instead of the expected international domain name This is normal Punycode-encoded output — most browsers decode and display the readable Unicode version in the address bar, but raw logs, some older tools, or copy-pasted URLs may show the encoded xn-- form instead
A domain looks legitimate in the browser but the underlying xn-- form reveals unexpected characters Potential homograph attack — see the security note below; visually similar characters from different scripts (e.g., Cyrillic "а" vs. Latin "a") can be used to register deceptive lookalike domains
Email or messaging app shows the raw xn--... string instead of a readable domain name The application isn't decoding Punycode for display, showing the DNS-level representation directly instead of the user-friendly Unicode form

Compatibility & gotchas — including a real security concern

  • Homograph attacks are a genuine, well-documented security risk tied to IDN/Punycode. Because many different Unicode characters look visually identical or nearly identical to ASCII letters (Cyrillic "а" vs. Latin "a" being the classic example), it's possible to register a domain that displays as looking exactly like a legitimate one but is actually a completely different domain underneath, at the Punycode/DNS level. This has been used in real phishing campaigns. Browsers have implemented various mitigations (displaying the raw Punycode form instead of the deceptive Unicode rendering under certain suspicious conditions), but it remains an active area of concern for security-conscious URL handling.
  • The xn-- prefix is a strong, reliable signal that a domain label contains encoded non-ASCII content — worth specifically checking for when validating or displaying domains in security-sensitive contexts.
  • Distinct from percent-encoding — a URL can need both: Punycode for an internationalized domain name, and percent-encoding for the path/query/fragment, operating independently on different parts of the same URL.
  • Not every browser/application handles Punycode display consistently — some show the decoded Unicode form by default, others show the raw xn-- encoded form, and behavior can vary based on the specific script/character combination involved (related to the homograph-attack mitigations mentioned above).

Fixing it / working with it

  • Manually decoding a Punycode domain: most programming languages have a Punycode/IDNA library — Python's domain.encode('idna') / .decode('idna'), or dedicated idna packages for more complete Unicode IDNA2008 support.
  • Security-conscious domain validation: if building anything that displays or validates user-supplied domains, be aware of the homograph-attack risk and consider showing the Punycode xn-- form explicitly (or flagging mixed-script domains) rather than always rendering the potentially-deceptive Unicode form.

Comparison

Scheme Key difference from Punycode
URL/percent-encoding Handles the path, query, and fragment portions of a URL; Punycode handles the domain name specifically — the two operate on different parts of the same URL
Base64 A general-purpose binary-to-text scheme, not specialized for the specific ASCII-compatibility constraints of DNS domain labels

FAQ

Why do some URLs show "xn--" instead of the actual domain name?

That's Punycode-encoded output — DNS only supports ASCII, so internationalized domain names (containing non-ASCII characters) get converted to an ASCII-safe xn---prefixed form for actual DNS resolution. Most browsers decode this back to the readable Unicode form for display, but raw logs or some tools may show the encoded form directly.

Is Punycode a security risk?

Punycode itself is just an encoding mechanism, but it enables a real security concern called homograph attacks — visually similar characters from different scripts can be used to register domains that look identical to legitimate ones in a browser's address bar while pointing to a completely different domain underneath. Browsers have implemented mitigations, but it remains worth being aware of.

How is Punycode different from percent-encoding in a URL?

They handle different parts of a URL entirely — Punycode encodes the domain name (hostname) portion specifically, addressing DNS's ASCII-only constraint, while percent-encoding handles the path, query string, and fragment. A single URL with an internationalized domain and non-ASCII query parameters would use both mechanisms simultaneously, each on its own part.

Fun fact

Punycode's name is a playful blend of "Punjabi" (referencing an early motivating use case for non-ASCII domain support) and "Unicode" — though the actual algorithm is entirely general-purpose and has nothing specifically to do with Punjabi text; the name stuck as an internal project reference during development and made it into the final RFC.