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 → 文本解碼還原字元。