2026年6月30日

2026年6月30日

日本語ドメインの変換方法|Punycode(xn--)との相互変換をコマンド・PHP・Python・JavaScriptで行う

日本語ドメインを DNS やサーバーの設定に書くときは、「xn--」で始まる Punycode(ASCII 形式)に変換して使います。変換は Web の変換ツールでもできますが、サーバー作業やプログラムの中で扱うなら、コマンドや各言語の標準的な関数で変換し、その結果を DNS で確かめるのが手堅い方法です。この記事では、変換の仕組みを押さえたうえで、コマンドライン・PHP・Python・JavaScript での変換方法と、変換結果が食い違うときの原因、WordPress で使うときの注意点を説明します。

例として、JPRS の案内でもよく使われる「日本語.jp」を使います。変換すると xn--wgv71a119e.jp になります。

変換の仕組み:ラベルごとに ASCII へ置き換える

ドメイン名は「.」で区切られた「ラベル」の集まりです。日本語ドメインの変換(IDNA)は、ラベルごとに次の処理を行います。

  1. 文字を正規化する(全角英数字を半角に、大文字を小文字にそろえるなど)
  2. 使ってよい文字かを検査する
  3. 英数字以外を含むラベルだけを Punycode に符号化し、先頭に「xn--」を付ける

そのため、「WWW.日本語.JP」のように全角や大文字が混ざっていても、変換後は www.xn--wgv71a119e.jp になります。英数字だけのラベル(www や jp)はそのまま残り、日本語のラベルだけが xn-- の形になる、という点を覚えておくと、変換結果を見たときに正しいかどうかを判断しやすくなります。都道府県型 JP ドメインの「例え.福岡.jp」なら、「例え」と「福岡」の両方が変換され、xn--r8jz45g.xn--4it168d.jp になります。

逆方向(Punycode から日本語へ戻す)の変換もあります。サーバーのログや証明書、メールのヘッダーには xn-- の形で記録されるので、調査のときはこちらをよく使います。

変換方式の違いに注意する

変換のルールには古い「IDNA2003」と、現在の「IDNA2008」があり、さらに実際のブラウザやライブラリの多くは Unicode の「UTS #46」という互換処理を組み合わせています。日本語の一般的な文字ではどちらでも同じ結果になることが多いのですが、ドイツ語の「ß」のように、方式によって結果が変わる文字があります。

  • IDNA2003 では「faß.de」が fass.de に置き換えられる
  • IDNA2008(UTS #46 の非移行処理)では xn--fa-hia.de になる

日本語ドメインだけを扱う場合でも、使うツールやライブラリが古い方式のままだと、記号や異体字を含む名前で結果が食い違うことがあります。複数の道具で結果を比べ、一致しない場合は新しい方式(IDNA2008/UTS #46)に合わせるのが基本です。

コマンドラインで変換する

Linux では libidn2 の idn2 コマンドが使えます。パッケージ名はディストリビューションによって異なるので、パッケージマネージャーで「idn2」を検索してください。

# 日本語 → Punycode
idn2 日本語.jp
# xn--wgv71a119e.jp

# Punycode → 日本語
idn2 -d xn--wgv71a119e.jp
# 日本語.jp

ターミナルの文字コードが UTF-8 でないと、入力した日本語が化けて誤った結果になります。おかしな結果が出たら、まず locale で UTF-8 になっているかを確認します。

変換した名前が実際に DNS に登録されているかは、Punycode のまま dig に渡して確かめます。

dig +short A xn--wgv71a119e.jp
dig +short NS xn--wgv71a119e.jp

dig は環境によって日本語の入力を自動で変換したり、しなかったりします。設定ミスの切り分けでは、変換済みの文字列を渡したほうが、どこで間違えたのかが分かりやすくなります。

PHP で変換する

PHP では intl 拡張の idn_to_ascii() と idn_to_utf8() を使います。intl が入っていないサーバーでは関数そのものが存在しないので、function_exists() で確認してから使います。

<?php
if ( ! function_exists( 'idn_to_ascii' ) ) {
    exit( 'intl 拡張が有効になっていません' );
}

// 日本語 → Punycode(UTS #46 の非移行処理)
$ascii = idn_to_ascii( '日本語.jp', IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46 );
if ( false === $ascii ) {
    exit( '変換できない文字が含まれています' );
}
echo $ascii, PHP_EOL; // xn--wgv71a119e.jp

// Punycode → 日本語
$unicode = idn_to_utf8( 'xn--wgv71a119e.jp', IDNA_NONTRANSITIONAL_TO_UNICODE, INTL_IDNA_VARIANT_UTS46 );
echo $unicode, PHP_EOL; // 日本語.jp

つまずきやすいのは、URL 全体をそのまま渡してしまうことです。この関数が受け取るのはホスト名だけです。WordPress のコードの中で URL から変換する場合は、wp_parse_url() でホスト部分を取り出してから渡します。また、変換に失敗すると false が返るので、結果をそのまま設定値や画面に使わず、失敗時の処理を書いておきます。画面に表示する場合は、ほかの値と同じく esc_html() などでエスケープします。

Python で変換する

Python の標準ライブラリにも "日本語.jp".encode("idna") という書き方がありますが、これは古い IDNA2003 による変換です。新しい方式で変換したい場合は、外部パッケージの idna を使います。

# pip install idna
import idna

print(idna.encode("日本語.jp", uts46=True))          # b'xn--wgv71a119e.jp'
print(idna.decode("xn--wgv71a119e.jp"))              # 日本語.jp
print(idna.encode("WWW.日本語.JP", uts46=True))   # b'www.xn--wgv71a119e.jp'

uts46=True を付けると、全角や大文字の正規化を行ってから変換します。付けない場合、正規化されていない入力はエラーになることがあります。戻り値はバイト列なので、文字列として使うときは .decode("ascii") で変換します。

JavaScript(ブラウザ・Node.js)で変換する

ブラウザでも Node.js でも、URL を使うとホスト名が自動で Punycode に変換されます。

const u = new URL('https://日本語.jp/');
console.log(u.hostname); // xn--wgv71a119e.jp

Node.js では url モジュールの domainToASCII() と domainToUnicode() も使えます。

const { domainToASCII, domainToUnicode } = require('node:url');
console.log(domainToASCII('日本語.jp'));            // xn--wgv71a119e.jp
console.log(domainToUnicode('xn--wgv71a119e.jp'));  // 日本語.jp

古い記事では Node.js 組み込みの punycode モジュールが紹介されていることがありますが、このモジュールは非推奨になっています。また、punycode の toASCII() は正規化を行わないため、全角文字を含む入力では上の方法と結果が変わります。新しく書くコードでは URL か domainToASCII() を使います。

変換結果が合わないときの確認順

変換したのに名前解決できない、証明書のエラーが出る、といったときは、次の順に確認します。

  1. ラベルの区切りが正しいか:「。」(全角の句点)や全角ピリオドが混ざっていないか。UTS #46 の処理ではこれらを区切りとして扱うことがありますが、手作業でコピーした文字列では思わぬ形になることがあります。
  2. ツールによって結果が違わないか:上の複数の方法で変換し、同じ結果になるかを比べます。違う場合は、古い方式で変換している道具がないかを疑います。
  3. 登録した名前と一致しているか:レジストラの管理画面や whois に表示される Punycode と照らし合わせます。似た漢字(異体字)や、ひらがなとカタカナの取り違えはよくある原因です。
  4. DNS に登録されているか:dig に Punycode を渡し、レコードが返るかを確認します。

なお、ブラウザは見た目の似た文字を使ったなりすましを防ぐため、条件によっては日本語ではなく xn-- の形でアドレスバーに表示します。これは変換の失敗ではなく、ブラウザの表示ルールによるものです。

WordPress で使うときの注意点

WordPress の「設定」→「一般」にあるサイトアドレスや、wp-config.php で WP_HOME・WP_SITEURL を指定する場合は、Punycode の形(https://xn--wgv71a119e.jp など)で書くと、プラグインや外部サービスとの間で文字化けや不一致が起こりにくくなります。Web サーバーの server_name や ServerName、SSL 証明書の申請に使う名前も Punycode で書きます。

既存サイトのアドレスを日本語表記から Punycode 表記に書き換える場合は、データベースの中の URL もそろえる必要があります。作業前にファイルとデータベースのバックアップを取り、ステージング環境で試してから本番で行います。WP-CLI を使うなら、まず --dry-run で置換される件数を確認します。

wp search-replace 'https://日本語.jp' 'https://xn--wgv71a119e.jp' --dry-run

変換の仕様やブラウザの表示ルール、ライブラリの対応状況は更新されることがあります(2026年10月現在)。本番で使う前に、利用しているサーバーの PHP や Node.js のバージョンで、実際に変換結果を確かめておくと安心です。

まとめ

  • 日本語ドメインは、ラベルごとに正規化と検査をしたうえで、英数字以外を含むラベルだけが「xn--」付きの Punycode に変換されます。
  • 変換方式には IDNA2003 と IDNA2008(UTS #46)があり、文字によって結果が変わります。新しい方式に合わせるのが基本です。
  • コマンドは idn2、PHP は idn_to_ascii()(intl 拡張)、Python は idna パッケージ、JavaScript は URL や domainToASCII() が使えます。
  • 変換後は Punycode のまま dig で DNS を確認し、うまくいかないときは区切り文字・ツールの違い・登録名の順に確かめます。
  • WordPress やサーバーの設定には Punycode で書き、URL を書き換えるときはバックアップとステージングでの確認を先に行います。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲