開発中のサーバー環境で、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/firewalld | OUTPUTや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.com
を127.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
や .htaccess
が 127.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や管理系スクリプトを実装する際の一助になれば幸いです。
コメント