「Could not connect to host」は、SOAP 開発で最もよく遭遇するエラーの一つです。PHP の SoapClient、Python の zeep、Java の JAX-WS、.NET の WCF など、あらゆる SOAP クライアントライブラリで発生します。このエラーの意味はシンプルで、クライアントが SOAP サービスのエンドポイントへの TCP 接続に失敗したということです。
SOAP Fault とは異なり、リクエストがサービスに到達していないことを示すネットワーク層の問題です。本記事では、現実的な原因をすべて網羅し、各言語での具体的な修正コードを紹介します。
エラーの表示形式
言語やライブラリによって表示が異なりますが、根本原因は同じです。
# PHP SoapClient
SoapFault: Could not connect to host
# Python zeep
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded
# Java JAX-WS
com.sun.xml.ws.client.ClientTransportException:
HTTP transport error: java.net.ConnectException: Connection refused
# .NET WCF
System.ServiceModel.EndpointNotFoundException:
Could not connect to http://example.com/service
原因 1: DNS 解決の失敗
WSDL エンドポイント URL のホスト名が IP アドレスに解決できない場合、TCP ハンドシェイクの前に接続が失敗します。Docker コンテナ、Kubernetes Pod、内部 DNS 名を使用する SOAP サービスで頻繁に発生します。
診断方法:
# ホスト名が解決できるか確認
nslookup soap-service.internal.corp
dig soap-service.internal.corp
# Docker コンテナ内から確認
docker exec -it app-container nslookup soap-service.internal.corp
# Kubernetes Pod 内から確認
kubectl exec -it pod-name -- nslookup soap-service.internal.corp
修正例 (PHP):
// DNS が失敗する場合、IP アドレスと Host ヘッダーを使用
$context = stream_context_create([
'http' => [
'header' => "Host: soap-service.internal.corp\r\n",
],
]);
$client = new SoapClient(null, [
'location' => 'http://10.0.1.50:8080/service',
'uri' => 'http://soap-service.internal.corp/service',
'stream_context' => $context,
]);
修正例 (Python):
from zeep import Client
from zeep.transports import Transport
from requests import Session
session = Session()
transport = Transport(session=session)
client = Client('service.wsdl', transport=transport)
# サービスアドレスを解決済み IP に変更
client.service._binding_options['address'] = 'http://10.0.1.50:8080/service'
原因 2: ファイアウォールまたはポートのブロック
ネットワークファイアウォール、AWS のセキュリティグループ、Kubernetes のネットワークポリシーなどにより、アプリケーションサーバーから SOAP サービスへの送信接続がブロックされている場合があります。DNS の失敗とは異なり、ファイアウォールによるブロックは通常、接続タイムアウトを引き起こします。
診断方法:
# TCP 接続テスト(HTTP を送信せずに確認)
nc -zv soap-service.example.com 443
# curl でタイムアウト付きテスト
curl -v --connect-timeout 5 https://soap-service.example.com/service?wsdl
対処法: インフラ側の変更が必要ですが、タイムアウトを設定して早期にフェイルさせることができます。
// PHP: タイムアウトを設定して長時間の待機を回避
$client = new SoapClient('service.wsdl', [
'connection_timeout' => 10,
'stream_context' => stream_context_create([
'http' => ['timeout' => 15],
]),
]);
// Java JAX-WS: リクエストコンテキストでタイムアウトを設定
BindingProvider provider = (BindingProvider) port;
Map<String, Object> ctx = provider.getRequestContext();
ctx.put("com.sun.xml.ws.connect.timeout", 10000); // 10 秒
ctx.put("com.sun.xml.ws.request.timeout", 30000); // 30 秒
原因 3: SSL/TLS ハンドシェイクの失敗
SOAP サービスが HTTPS を使用していて TLS ハンドシェイクが失敗した場合、ほとんどのクライアントライブラリは SSL 固有のエラーではなく、接続エラーとして報告します。
診断方法:
# SSL 診断
openssl s_client -connect soap-service.example.com:443 -servername soap-service.example.com
# TLS バージョンの確認
openssl s_client -connect soap-service.example.com:443 -tls1_2
修正例 (PHP):
$context = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'cafile' => '/etc/ssl/certs/ca-certificates.crt',
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
],
]);
$client = new SoapClient($wsdl, [
'stream_context' => $context,
]);
修正例 (Python):
from zeep import Client
from zeep.transports import Transport
from requests import Session
session = Session()
session.verify = '/path/to/ca-bundle.crt'
transport = Transport(session=session)
client = Client(wsdl_url, transport=transport)
修正例 (Java):
# サーバーの CA 証明書を truststore にインポート
keytool -import -trustcacerts -file server-ca.crt -alias soap-server -keystore truststore.jks
System.setProperty("javax.net.ssl.trustStore", "/path/to/truststore.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
原因 4: プロキシ設定の不備
企業環境では、外部への HTTP/HTTPS トラフィックがプロキシサーバーを経由する必要があることが一般的です。アプリケーションがプロキシの存在を認識していない場合、外部 SOAP サービスへの直接接続は失敗します。
診断方法:
# プロキシ環境変数の確認
echo $HTTP_PROXY $HTTPS_PROXY
# プロキシ経由で接続テスト
curl -x http://proxy.corp:8080 https://soap-service.example.com/service?wsdl
修正例 (PHP):
$context = stream_context_create([
'http' => [
'proxy' => 'tcp://proxy.corp:8080',
'request_fulluri' => true,
],
]);
$client = new SoapClient($wsdl, [
'stream_context' => $context,
'proxy_host' => 'proxy.corp',
'proxy_port' => 8080,
]);
修正例 (Python):
session = Session()
session.proxies = {
'http': 'http://proxy.corp:8080',
'https': 'http://proxy.corp:8080',
}
transport = Transport(session=session)
client = Client(wsdl_url, transport=transport)
原因 5: WSDL 内のエンドポイント URL が不正
一部の WSDL ファイルには、サーバー側から見れば正しいが、クライアントのネットワークからは到達できないエンドポイント URL(例: http://localhost:8080/service)が記述されています。ロードバランサーやリバースプロキシの背後にあるサービスで頻発します。
診断方法:
# WSDL からエンドポイント URL を抽出
curl -s https://soap-service.example.com/service?wsdl | grep -i "location\|address"
<soap:address location="..."> が localhost や内部ホスト名を指している場合、それが原因です。
修正例 (PHP):
// エンドポイントの location をオーバーライド
$client = new SoapClient('https://soap-service.example.com/service?wsdl', [
'location' => 'https://soap-service.example.com/service',
]);
修正例 (Java):
BindingProvider provider = (BindingProvider) port;
provider.getRequestContext().put(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"https://correct-endpoint.example.com/service"
);
原因 6: サービス停止またはリトライの未実装
最もシンプルな原因として、SOAP サービス自体がメンテナンス中、クラッシュ、または URL 変更されている場合があります。
リトライロジックの実装 (PHP):
function callSoapWithRetry(SoapClient $client, string $method, array $params, int $maxRetries = 3)
{
$attempt = 0;
while (true) {
try {
return $client->__soapCall($method, $params);
} catch (SoapFault $e) {
if (strpos($e->getMessage(), 'Could not connect to host') === false
|| $attempt >= $maxRetries) {
throw $e;
}
$attempt++;
$delay = pow(2, $attempt);
error_log("SOAP 接続失敗、{$delay} 秒後にリトライ ({$attempt}/{$maxRetries})");
sleep($delay);
}
}
}
診断フローチャート
「Could not connect to host」に遭遇した場合、次の順序で切り分けを行います。
1. ホスト名を解決できるか
$ nslookup hostname → NG → DNS 設定を修正 / IP を使用
→ OK ↓
2. ポートに到達できるか
$ nc -zv hostname port → NG → ファイアウォール / セキュリティグループ確認
→ OK ↓
3. TLS ハンドシェイクが成功するか
$ openssl s_client -connect hostname:port → NG → SSL 設定を修正
→ OK ↓
4. HTTP レスポンスがあるか
$ curl -I https://hostname/service → NG → サービス停止 / URL 不正
→ OK ↓
5. WSDL 内のエンドポイント URL は正しいか
$ curl -s wsdl_url | grep address → 不一致 → エンドポイントをオーバーライド
6. プロキシが必要か
$ curl -x proxy_url https://hostname → NG → プロキシ設定を追加
SOAPless による解決
SOAP サービスへの接続問題は、本質的にアプリケーションから SOAP エンドポイントへの直接ネットワークアクセスが必要であることに起因します。DNS、ファイアウォール、SSL 証明書、プロキシの設定を、すべてのデプロイ環境で正しく構成しなければなりません。
SOAPless はこの構造を逆転させます。WSDL URL を SOAPless ダッシュボードに登録すると、SOAPless のインフラストラクチャから SOAP サービスへの接続が管理されます。アプリケーションは SOAPless の REST API に接続するだけで済みます。
# SOAP 接続のデバッグの代わりに、シンプルな REST コール
curl -X POST https://api.soapless.com/v1/your-service/GetUser \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"userId": "12345"}'
アプリケーションから SOAP エンドポイントへの直接接続が不要になるため、ファイアウォール、DNS、SSL、プロキシに関するトラブルシューティングから解放されます。