Punycode / IDN 转换器

把 Unicode 文本编码为 ASCII 的 Punycode(RFC 3492,也就是 xn-- 国际化域名背后的编码),或把 xn-- 标签 / 域名解码回原始字符。 可以输入单个标签(如 中文)或整个带点的域名(如 例え.jp);每个非 ASCII 标签都会被独立编码。 另见:URL 编码器 | Ascii85 转换器 | Base32 / Base58 编码转换器。
全部处理都在你的浏览器本地完成 —— 不上传任何内容。
从没听说过 Punycode?什么是 Punycode —— 大白话讲解(附示例、常见误解和真正会咬人的规则)。
工作原理: 凡含非 ASCII 字符(中日韩、带音符的拉丁字母、emoji 等)的标签都会被转换为 ASCII Punycode 并加上 xn-- 前缀。纯 ASCII 标签(以及结尾点号产生的空段)原样通过。只含基本 ASCII 字符的标签永远不会变。
示例: 中文 → xn--fiq228c  ·  例え.jp → xn--r8jz45g.jp  ·  bücher.de → xn--bcher-kva.de  ·  😂.com → xn--e28h.com
工作原理: 凡以 xn-- 开头(前缀大小写不敏感)的标签都会被解码回 Unicode;其他 ASCII 标签原样通过。编码部分(最后一个连字符之后)出现大写字母 A-Z 属于歧义,会被拒绝 —— 请粘贴小写的 A 标签。
在输入框里按 Ctrl+Enter 即运行当前模式。
选择一个方向并运行。所有处理都在你的浏览器内完成。
RFC 3492 Punycode 速览:
Punycode 是一种 bootstring 编码:先原样复制基本 ASCII 码点(字母、数字、连字符),然后每插入一个非 ASCII 码点,就用带偏移自适应的可变长度 base-36 编码写成一小段整数(数字为 a-z 后接 0-9)。在域名里,每个编码后的标签都加上 xn-- 前缀,浏览器和 DNS 才能把它与普通标签区分开。码点 - 充当复制部分与编码部分之间的分隔符。

已知向量 —— 中文 → xn--fiq228c,例え → xn--r8jz45g,中国 → xn--fiqs8s,bücher → xn--bcher-kva。
什么是 Punycode?
一句话:Punycode 是一种拼写方法,用域名系统当初设计时只认的 37 个字符 —— a-z、0-9 和 - —— 来拼写非纯英文字母的字符:中文、带音符的拉丁字母、西里尔字母、emoji。它由 RFC 3492 定义;在域名里,你靠 xn-- 前缀认出它:中文 会变成 xn--fiq228c。

所以 Punycode 不是一种语言、一款字体,也不是国家代码 —— 它是同一个名字的两种拼法之间的翻译。Unicode 拼法叫 U 标签(中文.com、bücher.de),ASCII 拼法叫 A 标签(xn--fiq228c.com、xn--bcher-kva.de);把国际化名字塞进 DNS 的整套构想叫 IDN(国际化域名),建立在它之上的那套规则叫 IDNA。

它存在的原因是:DNS 在 1980 年代初被设计成一个刻意极小的字母表上的系统。一个要在 DNS 里穿行、进入区域文件、解析器缓存、日志行或 TLS 证书的主机名,必须是纯 ASCII —— 线路格式根本没有给其他东西留字段。Punycode 就是那个逃生口:它让一个不是 ASCII 的名字能够“装作” ASCII 通行,并让接收端精确还原原始字符。这就是中文域名能在浏览器里工作的原因,也是为什么在每一个无法发送 Unicode 拼法的地方,你都会看到 xn-- 这一个字符串。

30 秒看懂 Punycode
你输入的(U 标签) DNS 拿到的(A 标签) 含义
中文.comxn--fiq228c.com“中文”二字
中国xn--fiqs8s中文的“中国”
例え.jpxn--r8jz45g.jp日语示例
bücher.dexn--bcher-kva.de德语 bücher,“书”
münchen.dexn--mnchen-3ya.de慕尼黑
😂.comxn--e28h.comemoji 标签 —— 编码上合法,多数注册局不允许

一个值得记住的长度对比:中文 是 2 个字符、UTF-8 下 6 字节,而 xn--fiq228c 是 11 个 ASCII 字节。Punycode 通常让名字变长,绝不会变短。

编码到底是怎么工作的

拿 bücher 来看这三步。这个引擎叫做 bootstring,整个算法只有几十行代码(就在本页的脚本里,可以直接读)。

  1. 把普通字符复制到前面。每个基本 ASCII 字符都按原顺序复制并保持可读:bücher → bcher。因为有东西被复制且还有剩余,所以要补一个 - 作为分界:bcher-。
  2. 把剩下的写成 base-36 数字。剩余字符按码点从小到大处理,每个都写成 base 36 的可变长度数字,其中 a=0 … z=25、0=26 … 9=35。对 bücher 而言,剩下的 ü 变成数字 kva。
  3. 贴上前缀。编码部分接到复制部分之后得到 bcher-kva,在域名语境里写作 xn--bcher-kva。

这套设计的巧妙之处在于那些 base-36 数字里存的不是字符本身,而是一个增量(delta) —— 当前字符离上一个已编码码点有多远,以及它在字符串中的位置。编码器按码点升序遍历,每段数字描述一个位置和一个距离。每编码完一个字符,一个叫 偏移(bias)的共享数字都会被调整,让后面的数字用尽可能少的位数。

上面就能亲眼看到的两个推论:

  • 相近的字符很便宜。因为只存距离,同一字符连成一串几乎不花什么:30 个 中 编码成 xn--fiqaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,只有 36 字节。精心挑选的一组彼此相近的汉字,能把大约 41 个字符塞进一个合法的 63 字节标签;单个字符反复重复则能达到 57 个。
  • 分散的字符很贵。离上一个字符越远,增量数字越长。字符种类繁多的中文文本通常每个字符要多花大约 2-3 个字母,这就是为什么即便标签上限是 63 字节,中日韩标签在 20-40 个字符左右就会耗尽空间。

解码就是同一套配方的逆行:复制 ASCII 前缀,读出数字,把增量还原成码点,并把每个新字符插到增量所编码的位置上。bootstring 里只有小写数字,所以编码部分出现大写字母会被直接拒绝而不是靠猜 —— DNS 大小写不敏感,两种大小写都允许会让两种不同拼法变得无法区分。

你实际会在哪里遇到它
  • 地址栏里。浏览器可能显示 xn--fiq228c.com 而不是 中文.com。这是同一个站点。当域名混用多种文字时,一些浏览器会刻意显示 Punycode 形式,正是为了不让相似字符(西里尔 а 与拉丁 a)藏在 URL 里。
  • DNS 本身里。区域文件、dig / nslookup / DoH 应答和解析器日志里只会出现 xn-- 形式;纯 Unicode 会被拒绝或被悄悄弄坏。
  • TLS 证书里。国际化域名的使用者备选名称(SAN)以 A 标签存储(DNS:xn--fiq228c.com),所以证书看起来与你输入的名字不一样是正常的,不是配置错误。
  • 配置与工具链里。Web 服务器的 server_name / 虚拟主机条目、Cookie 域、主机与 CDN 控制台、邮件域、绑定到域名的 API 密钥、数据库键,用 A 标签形式都更稳妥。
  • 测试与数据管线里。有些库会把 Unicode 直接丢给解析器然后失败;先转成 Punycode 是标准修法。而且这个映射是规范的 —— 一个 Unicode 标签恰好对应一个 A 标签 —— 所以 xn-- 形式也是日志和检索中稳定的标识符。
Punycode 不是什么

Punycode 只是一种可逆的拼法,仅此而已。它不是加密,也不是安全特性:任何人都能用上面的工具解码它,所以 xn-- 标签只说明“这里编码过”,对归属者只字未提。它也不是压缩 —— 通常反而让文本变长,就像上面 6 字节变 11 字节的例子。它也不等于 Unicode 在互联网上旅行的其他方式;那些解决的是问题的不同层面。

方案 把什么变成什么 用在哪里 中文 变成
Punycode(RFC 3492)Unicode 域名标签 → ASCII 字母与数字仅限国际化域名标签xn--fiq228c
UTF-8(RFC 3629)码点 → 字节所有文件与 Web 请求E4 B8 AD E6 96 87(6 字节)
百分号编码(RFC 3986)字节 → %XX 转义URL 路径、查询串、表单体%E4%B8%AD%E6%96%87
Base64(RFC 4648)字节 → 64 个 ASCII 字符文本、JSON、邮件、data URL 里的二进制5Lit5paH
HTML 实体码点 → 命名或数字引用HTML 与 XML 源码中文
Unicode 转义码点 → \uXXXX源码与 JSON 字符串字面量\u4e2d\u6587

实用结论:Punycode 只作用于 URL 的域名标签,不作用于整个 URL。路径与查询串是另一个问题,用的是百分号编码(见URL 编码器)。

真正会咬人的限制与规则
  • 大小限制来自 DNS,而不是 Punycode。一个标签最多 63 字节,完整名字最多 255 字节(RFC 1034 / 1035)。A 标签是纯 ASCII,字节数就是字符数,每个非 ASCII 字符都要花掉一部分预算 —— 大约每 1-4 个 Punycode 字母花掉 1 个字符的预算。
  • Punycode 什么都编码;注册局只允许一部分。IDNA 规则叠在上面,限制哪些码点根本不允许注册 —— 大写字母、空格、! 这类标点以及 emoji 在真实域名里都不合法。本页执行的是 RFC 3492 转换,所以它会愉快地编码注册局会拒绝的文本。
  • 一个标签里只用一种文字。正是因为相似字符攻击,IDNA 不鼓励在单个标签内混用文字。一个名字可以被编码却仍然不可用,或可用却被浏览器列入黑名单。
  • 不要二次编码。xn-- 后面跟着已编码文本是无效的;纯 ASCII 的标签则完全不会变 —— 没有可编码的东西,自然不会加前缀。
  • 结尾的点号没问题。例え.jp. 是完全限定域名,末尾的空标签会保留;中间的空标签则不是有效主机名。
快问快答

“为什么我的中文域名在地址栏里显示成 xn--fiq228c.com?”

因为浏览器选择渲染 A 标签。站点是同一个;用本页的解码方向就能看到 Unicode 形式,或者把 Unicode 形式编码一遍,核对两种拼法是否一致。

“我需要改 DNS 记录、证书或主机配置吗?”

不用。凡是机器读名字的地方(DNS、证书、配置)都保留 xn-- A 标签,只有人读的地方才用 Unicode 形式。

“为什么结果比我输入的长?”

因为编码携带的不是字符本身,而是 base-36 的距离数字,而 36 个字符的字母表要拼出可能很大的数。变长是预期行为,不是 bug。

“为什么解码会报‘大写字母’?”

因为 bootstring 的数字定义为只允许小写(a-z、0-9)。考虑 DNS 大小写不敏感后,最后一个连字符之后出现大写 A-Z 是有歧义的,所以直接报告而不是默默去猜。

“Punycode 适用于电子邮件地址 @ 前面的部分吗?”

只有域名部分是 DNS 名字,才能用 Punycode。本地部分由邮件系统处理,不归 DNS 管,所以这种编码不适用于它。

“两个不同的 Unicode 名字会产生同一个 A 标签吗?”

不会。这种编码是单射的:不同的码点序列一定产生不同的 bootstring,所以解码 xn--fiq228c 得到的恰好是 中文,不会是别的。可能撞车的是外观 —— 长得像的不同字符 —— 那是 Unicode 字符集的属性,不是编码的属性。

本页遵循的标准
  • RFC 3492 —— Punycode,本页实现的 bootstring 编码。上面引用的已知向量都用该文档中的样例字符串校验过。
  • RFC 5890、RFC 5891、RFC 5892 与 Unicode UTS #46 —— IDNA2008 及其浏览器侧解释,决定哪些字符合法、标签如何规范化、文字如何混用的规则。
  • RFC 1034 与 RFC 1035 —— 从 DNS 继承的 63 字节标签与 255 字节名字限制。
  • RFC 3490 与 RFC 3491 —— 更早的 IDNA2003 一代,已被 IDNA2008 取代。如果某个名字在旧软件里表现不同,通常就是这个原因。

范围说明:本工具只做 RFC 3492 转换,别无其他。它不是 IDN 校验器,也不检查注册规则、价格或域名的可注册性 —— 而且由于它从不联网,它也无法告诉你某个名字是否存在。

在上面试一试:粘贴 中文 并点击文本 → Punycode 编码看 A 标签,或粘贴 xn--fiq228c 并点击Punycode → 文本解码还原字符。