中級 PHP ajax ネットワーク Whois

URLからサーバー情報とWhois情報を調べる

URL を入力すると、ページのタイトル・meta タグ・IP アドレス・サーバーの所在地、そしてドメインの登録情報(Whois)をまとめて調べるツールです。PHP からほかのサーバーに問い合わせる方法がわかります。

このサンプルは2017〜2018年ごろに作成したものです。PHPのバージョンが上がり、そのままでは動かなくなっていたため、2026年に PHP 8.3 で動くように書き直し、解説も新しくしました。

DEMO 実際に動かしてみよう

調べたいサイトのURLを入力して「調べる」を押すと、ページのタイトル・meta タグ・IPアドレス・サーバーの場所・ドメインの登録情報(Whois)を表示します。

解説コードを読んでみよう

このサンプルでわかること

  • ドメイン名から IP アドレスやホスト名を調べる PHP の関数
  • 外部の API(ipinfo.io)から JSON を受け取って使う方法
  • 「ソケット通信」で whois サーバーに直接問い合わせる方法

使っているファイル

  • index.php入力画面(URL を ajax で request.php に送る)
  • request.phpいろいろな情報を調べて、表の HTML を返す
  • whois_lite.phpwhois サーバーに問い合わせる関数
  • list.phpドメインの末尾(.com / .jp など)ごとの whois サーバーの一覧

1. 入力画面から ajax で送る

「調べる」ボタンで、入力された URL を request.php に送ります。調べるのに数秒かかることがあるので、先に「調べています…」と表示しておきます。

index.php(20〜41行目) PHP
<script>
$(function () {
    $('#lookup').on('click', function () {
        var url = $('input[name="url"]').val();
        $('#whois').html('<p>調べています…</p>');

        $.ajax({
            url: 'request.php',        // 同じフォルダの request.php に送る
            type: 'POST',
            data: { url: url },
            timeout: 20000,
            dataType: 'html'
        })
        .done(function (data) {
            $('#whois').html(data);    // 返ってきた表をそのまま表示
        })
        .fail(function () {
            $('#whois').html('<div class="alert alert-danger">通信に失敗しました。</div>');
        });
    });
});
</script>

2. このサイトからのリクエストか確認する

request.php を、ほかのサイトから勝手に使われないようにします。ブラウザが送ってくる Referer(どのページから来たか)のホスト名が、このサイトのホスト名と同じときだけ処理を続けます。続けて、URL の形式もチェックし、ドメイン名だけを取り出します。

request.php(14〜26行目) PHP
// 1. このサイトのページから送られたものだけ受け付ける(Referer のホストを確認)
$referer = parse_url($_SERVER['HTTP_REFERER'] ?? '', PHP_URL_HOST);
$myhost  = parse_url('http://' . ($_SERVER['HTTP_HOST'] ?? ''), PHP_URL_HOST);
if (!$referer || strcasecmp($referer, (string)$myhost) !== 0) {
    stop('外部からのアクセスは受け付けていません。');
}

// 2. URL の形式を確認し、ドメイン名を取り出す
$url = trim((string)($_POST['url'] ?? ''));
if (!preg_match('#^https?://#i', $url) || filter_var($url, FILTER_VALIDATE_URL) === false) {
    stop('入力された文字がURLの形式ではありません。');
}
$domain = strtolower((string)parse_url($url, PHP_URL_HOST));

Referer は簡単に偽装できるので、完全な対策ではありません。「普通の使い方で、ほかのサイトに埋め込まれるのを防ぐ」程度のものだと考えてください。

3. IP アドレスとホスト名を調べる

gethostbyname() はドメイン名から IP アドレスを、gethostbyaddr() は IP アドレスからホスト名(サーバーの名前)を調べる関数です。インターネットの「住所録」である DNS に問い合わせています。

request.php(28〜34行目) PHP
// 3. ドメインから IP アドレスとホスト名を調べる
$ip   = gethostbyname($domain);   // 見つからないときはドメイン名がそのまま返る
$found = filter_var($ip, FILTER_VALIDATE_IP) !== false;
$host = $found ? gethostbyaddr($ip) : '';

// サーバーの内側(プライベートIP)には接続しない
$isPublic = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;

最後の行の $isPublic では、IP アドレスが「プライベートアドレス(社内ネットワークなど)」でないかを調べています。ユーザーが入力した URL にサーバーがアクセスする機能では、サーバー内部をのぞかれないよう、この確認が大切です。

4. ページのタイトルと meta タグを取り出す

file_get_contents() でページの HTML を取得し、正規表現でタイトルと <meta name="..." content="..."> を取り出します。

request.php(36〜53行目) PHP
// 4. ページのタイトルと meta タグを取得する
$site_title = '';
$meta = [];
if ($isPublic) {
    $ctx  = stream_context_create(['http' => ['timeout' => 5, 'follow_location' => 0]]);
    $html = @file_get_contents($url, false, $ctx);
    if (is_string($html) && $html !== '') {
        $html = mb_convert_encoding($html, 'UTF-8', 'ASCII,JIS,UTF-8,CP51932,SJIS-win');
        if (preg_match('@<title>([^<]++)</title>@i', $html, $m)) {
            $site_title = trim($m[1]);
        }
        // <meta name="..." content="..."> を全部取り出す
        preg_match_all('@<meta\s+name=["\']([^"\']+)["\']\s+content=["\']([^"\']*)["\']@i', $html, $mm, PREG_SET_ORDER);
        foreach ($mm as $one) {
            $meta[$one[1]] = $one[2];
        }
    }
}

5. サーバーの所在地を API で調べる

ipinfo.io という無料の API に IP アドレスを渡すと、そのサーバーがある国・都市・会社名が JSON で返ってきます。json_decode() で PHP のデータに変換して使います。

request.php(55〜60行目) PHP
// 5. IP アドレスからサーバーの所在地を調べる(ipinfo.io の無料API)
$details = null;
if ($isPublic) {
    $json = @file_get_contents("https://ipinfo.io/{$ip}/json", false, stream_context_create(['http' => ['timeout' => 5]]));
    $details = is_string($json) ? json_decode($json) : null;
}

6. whois サーバーを選んで問い合わせる

Whois(フーイズ)は、ドメインの登録者や有効期限などを調べるしくみです。.com なら Verisign、.jp なら JPRS のように、ドメインの末尾ごとに問い合わせ先のサーバーが違うので、list.php の一覧から選びます。co.jp と jp の両方に一致するときは、長いほう(より詳しいほう)を使います。

request.php(62〜85行目) PHP
// 6. ドメインの末尾に合う whois サーバーを選ぶ(長く一致したものを優先)
$server = '';
$best = 0;
foreach ($list as $tld => $whoisServer) {
    if (preg_match('/(^|\.)' . preg_quote($tld, '/') . '$/', $domain) && strlen($tld) > $best) {
        $server = $whoisServer;
        $best = strlen($tld);
    }
}

// 7. whois サーバーに問い合わせ、「項目: 値」の形に分ける
$result = [];
$data = $server !== '' ? whois_lite_query($domain, $server) : '';
foreach (explode("\n", $data) as $line) {
    $line = trim($line);
    if ($line === '') {
        continue;
    }
    if (str_contains($line, '>>>')) {   // ここから下は注意書きなので読み飛ばす
        break;
    }
    $tmp = explode(':', $line, 2);
    $result[] = [trim($tmp[0]), trim($tmp[1] ?? '')];
}

whois の問い合わせはとてもシンプルで、サーバーのポート43番に接続し、ドメイン名を1行送るだけです。fsockopen() で接続し、fwrite() で送り、fread() で返事を読みます。

whois_lite.php(11〜38行目) PHP
function whois_lite_query(string $query, string $server, int $timeout = 10): string
{
    if ($server === '') {
        return '';
    }
    $fp = @fsockopen($server, 43, $errno, $errstr, $timeout);
    if (!$fp) {
        return '';
    }
    stream_set_timeout($fp, $timeout);
    fwrite($fp, $query . "\r\n");
    $data = '';
    while (!feof($fp)) {
        $chunk = fread($fp, 8192);
        if ($chunk === false) {
            break;
        }
        $data .= $chunk;
        $info = stream_get_meta_data($fp);
        if ($info['timed_out']) {
            break;
        }
    }
    fclose($fp);

    // JPRS などは ISO-2022-JP(JIS)で返ってくるので UTF-8 に揃える
    return mb_convert_encoding($data, 'UTF-8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-win');
}

日本のドメイン(.jp)の whois 結果は、「JIS」という古い文字コードで返ってきます。そのまま表示すると文字化けするので、最後に mb_convert_encoding() で UTF-8 に変換しています。

PHP 8.3 で書き直したポイント

  • 旧版は PEAR というライブラリの Net_Whois を使っていました。これは「クラス名と同じ名前の関数をコンストラクタにする」という PHP 4 時代の書き方で、PHP 8 では動きません。ソケットで直接問い合わせる短い関数(whois_lite.php)に置き換えました。
  • 一致する whois サーバーがないとき、空の変数を count() に渡してエラーで止まっていたので、最初に $server = ''; で初期化するようにしました。
  • 出力する値はすべて h() でエスケープし、ほかのサイトの内容がそのまま HTML として動かないようにしました。
↑