Punycode conversion turns an internationalized domain label such as bücher.example into the ASCII form xn--bcher-kva.example that DNS, TLS certificates, and email systems actually transmit on the wire, and the most direct path from a PHP codebase to a correct conversion is to follow the RFC 3492 algorithm label by label. The Punycode Converter implements that exact algorithm with the published constants (base 36, tmin 1, tmax 26, skew 38, damp 700, initial bias 72, initial code point 128) so you can verify or generate an ASCII form for any IDN label without writing your own encoder. PHP developers typically hit this problem when sending mail to addresses on IDN domains, when storing hostnames in DNS or certificate fields, or when validating an xn-- string that arrived from user input.

php convert to punycode
PHP Convert to Punycode: A Quick Browser-Based Path

Why PHP Codebases Reach for Punycode

PHP handles text well, but the network layers underneath it speak ASCII. The Domain Name System stores labels as LDH (letters, digits, hyphens) and the SMTP envelope, TLS SNI extension, and HTTP Host header all expect an ASCII hostname. A user typing mañana.example in a registration form, an address imported from a CSV that contains CJK characters, or a webhook URL pasted from another system will not survive a round trip through gethostbyname, stream_socket_client, or a certificate authority unless each non-ASCII label has been encoded with the xn-- ACE prefix defined in RFC 3492.

PHP offers two main routes for this. The intl extension ships idn_to_ascii and idn_to_utf8, which apply IDNA 2003 or 2008 processing on top of the raw Bootstring algorithm, and a number of Composer libraries (such as rowbot/punycode) implement RFC 3492 directly. Both paths are correct, but both also bring layers of Unicode normalization and contextual rules that can surprise you. A small, deterministic, browser-based converter is useful as a reference: paste the same label, pick the same direction, and compare the output against what your PHP code produces.

How the Bootstring Encoding Works in Plain English

Punycode encodes a sequence of Unicode code points into a string of ASCII letters, digits, and hyphens. Existing ASCII characters in the label are copied through first; the encoder then inserts hyphens to mark the position where the non-ASCII characters begin and appends a sequence of variable-length "delta" values that reference the remaining code points in order. The deltas are written in a base-36 alphabet that mixes digits and lowercase letters, and a per-position bias adapts so that common characters stay compact while rare ones cost more characters.

The fixed constants matter. Base is 36, tmin is 1, tmax is 26, skew is 38, damp is 700, the initial bias is 72, and the first non-basic code point is 128. These are the values Punycode Converter uses, and they match every other compliant implementation, so a label that round-trips through the tool will also round-trip through PHP code that calls into the intl extension with the same flags. Where implementations diverge is in what they do before or after that algorithm: normalizing full-width dots, applying NFC or NFKC, dropping characters that the IDNA 2008 tables forbid, and so on.

Convert a Domain Step by Step

The tool keeps the workflow to three explicit steps so the input and output are easy to inspect.

  1. Paste only a domain name into the input box and choose either Unicode-to-ASCII or ASCII-Punycode-to-Unicode. Full URLs with a scheme, path, port, query, or fragment are out of scope, and Unicode full stops used in CJK text are normalized to an ASCII dot for you.
  2. Convert and inspect every label. The converter splits the domain on dots, processes each label independently, and only attaches the xn-- prefix to labels that contain non-ASCII code points. Plain ASCII labels are lowercased and otherwise left unchanged, so the output preserves label boundaries.
  3. Copy the result with the copy button (which copies only the converted domain, never a scheme or trailing slash), then validate it with the target registry, your URL library, or a certificate authority before you trust it. This is also the moment to look for look-alike characters from different writing systems.

Encode Mode vs Decode Mode

The two modes are mirror images, but their inputs and outputs differ in ways that matter when you are debugging a PHP workflow. The table below summarizes what each mode expects and what it returns.

AspectUnicode-to-ASCII (encode)ASCII-Punycode-to-Unicode (decode)
Typical inputReadable label such as bücher or 例子ASCII label beginning with xn--
OutputASCII label with xn-- prefix on non-ASCII labelsOriginal Unicode label, prefix consumed
Prefix behaviorxn-- is added only to labels that contain non-basic code pointsxn-- is required on the label being decoded; other ASCII labels are lowercased and left unchanged
ASCII-only labelsLowercased and left unchangedLowercased and left unchanged
When to use itBefore writing to DNS, SMTP RCPT TO, or a certificate signing requestBefore displaying or logging an xn-- hostname you received

PHP-Specific Pitfalls and What to Check After Converting

The biggest source of confusion between a PHP developer and a raw RFC 3492 converter is normalization. PHP's idn_to_ascii with the IDNA_DEFAULT or IDNA_UTS_46 flags runs the input through UTS #46 mapping tables before Bootstring encoding, which means a label that contains a compatibility character or a dot variant can produce a different xn-- form than the tool does. That is not a bug in either system: the WHATWG URL Standard also applies its own mapping before encoding. Treat the browser-based converter as the raw RFC 3492 reference and let your PHP code apply whatever IDNA layer your application requires on top of it.

The second pitfall is homograph risk. A valid conversion of xn--80ak6aa92e.com back to a Cyrillic-looking string does not prove that the domain belongs to the organization it resembles. Characters from different scripts can be visually identical, so before you trust any decoded label, confirm the writing system, the registry, and the organization behind the name. For a deeper walkthrough on producing the ASCII form for DNS, see the guide on how to convert Punycode to ASCII for domain names; for the reverse direction, the guide on converting Punycode back to readable Unicode shows how to inspect a suspicious xn-- hostname label by label.

A third pitfall is silent truncation. Punycode has no length cap built in; the 63-octet label limit lives in DNS, and IDNA adds its own restrictions on character categories. A label that converts successfully can still be too long, reserved by the registry, or blocked under the registry's IDN policy. If your registrar rejects the ASCII form, follow that policy rather than swapping characters and re-encoding.

Input Limits and Rejected Cases

The converter is intentionally narrow so that what it accepts matches what PHP and DNS expect. The following cases will be rejected with a clear error rather than silently mangled:

  • Empty labels (consecutive dots, leading dot, or trailing dot).
  • Malformed digits inside an encoded sequence.
  • Invalid Unicode scalar values such as lone surrogates.
  • Inputs longer than 1,000 code points in total.
  • Anything that is not a domain name: URLs with a scheme, paths, ports, query strings, or fragments.

Eight fixtures shipped with the implementation cover Latin accents, Greek, CJK characters, symbols, single-label and multi-label inputs (including bücher, mañana, 例子, 测试, παράδειγμα, and a combined snowman plus command-symbol example). Negative fixtures cover empty labels and incomplete encoded sequences so that the rejection paths are exercised. Because the iteration walks Unicode code points rather than UTF-16 halves, supplementary characters such as emoji survive the round trip intact, and the arithmetic is guarded against overflow during delta multiplication and weight accumulation. That determinism is what makes the converter useful as a reference point: the same input always produces the same output, and you can copy it directly into a PHP test fixture.

Putting It Together in a PHP Workflow

A practical pattern is to keep the converter as the verification step in a test harness rather than the production path. Use the intl extension or a vetted Composer library for the real conversion so that UTS #46, IDNA 2008, and registry policies are honored, and feed the same label into Punycode Converter as a sanity check during development. If the two ever disagree on the raw xn-- form, the difference will almost always be a normalization decision rather than a Bootstring bug, and that is a useful signal that you should pin down which IDNA mode your PHP code is using before you ship.