PHPで自分のドメインにcurlできない原因と、ループバックで安全にJSONを取得する方法

開発中のサーバー環境で、PHPから自分自身のドメイン(FQDN)にcurlでアクセスしようとしてタイムアウトになる現象に遭遇することがあります。
特に、HTTPS+Basic認証付きのJSONファイルなどをPHP経由で取得したい場合、この問題は厄介です。

本記事では、その原因と調査ポイント、そしてループバック(127.0.0.1)経由での安全な取得方法を詳しく解説します。


現象:PHPやcurlで自分のFQDNにアクセスするとタイムアウト

例:

curl -u user:pass https://example.com/secure/data.json

結果:

* connect to example.com port 443 failed: 接続がタイムアウトしました
curl: (7) Failed to connect to example.com port 443

PHPでも同様に、file_get_contents()curl_exec() を使っても接続できません。


原因:自分自身のグローバルIPにアクセスできない構成

多くのサーバー(特にクラウドVPSや社内ネットワーク)では、NATループバック(Hairpin NAT)が無効になっており、自分のFQDNを使って外向き→内向きの通信を行うと接続が失敗します。

よくある原因

項目内容
NATループバック不可自分のグローバルIP経由で戻れない
iptables/firewalldOUTPUTやFORWARDチェーンでHTTPS通信が制限
Apache/Nginx設定ローカルIPからのアクセスを拒否
DNS結果の違い外部では通るFQDNが、内部からはIP解決されず通信不可になることも

調査ポイント:どこで詰まっているかを確認する方法

以下の順で原因を切り分けると効率的です。

1. DNSの確認

dig example.com

→ 正しくグローバルIPが返っているか?

2. ポート疎通確認

nc -zv example.com 443

→ 「接続拒否」や「タイムアウト」ならFWやNATに問題の可能性

3. curlの詳細ログで確認

curl -v -u user:pass https://example.com/secure/data.json

Trying ... で止まる → 通信レベルで失敗
Connected 以降に 403 等が出る → Apache等のアプリケーション設定に問題


対策:ループバック(127.0.0.1)+SNI対応で安全にアクセスする

コマンドラインでの成功例(–resolve使用)

curl -v \
  --resolve example.com:443:127.0.0.1 \
  -u user:pass \
  https://example.com/secure/data.json
  • --resolve によって、example.com127.0.0.1 に強制解決
  • HTTPSのホスト名は維持されるため、SSL検証やSNIも問題なし

PHPでの実装(CURLOPT_RESOLVE)

<?php
$url = 'https://example.com/secure/data.json';
$username = 'your_user';
$password = 'your_password';

$host = 'example.com';
$ip = '127.0.0.1';
$port = 443;

$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_USERPWD => "$username:$password",
    CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_HTTPHEADER => [
        "Host: $host"
    ],
    CURLOPT_RESOLVE => [ "$host:$port:$ip" ],
]);

$response = curl_exec($ch);

if (curl_errno($ch)) {
    $error = curl_error($ch);
    error_log("cURL Error: $error");
    echo "エラーが発生しました:$error";
} else {
    header('Content-Type: application/json');
    echo $response;
}

curl_close($ch);

エラー確認とログ出力のポイント

  • curl_errno() でエラー番号を取得
  • curl_error() で詳細なエラーメッセージを記録
  • エラー内容は error_log() を使ってログに出力することで、Web画面には表示せずに安全に確認可能

Apache/Nginx側の注意点

Apacheの Require.htaccess127.0.0.1 を拒否していないか確認:

<Directory /var/www/html/secure>
    Require all granted
    # または
    Require ip 127.0.0.1
</Directory>

まとめ

  • サーバー自身からFQDN経由でcurlしたときのタイムアウトは、NATループバックの不備が原因であることが多い
  • curl --resolve や PHPの CURLOPT_RESOLVE によって、直接127.0.0.1にアクセスしつつ、ドメインベースの通信を維持できる
  • ローカルAPI連携や構成確認時には非常に有効な手法

このような「curlは通るがPHPからは通らない」「FQDNでは通らないがIP直打ちなら通る」という状況で、意外とハマりやすいこの問題。内部APIや管理系スクリプトを実装する際の一助になれば幸いです。

コメント

タイトルとURLをコピーしました