soap-errorstroubleshootingnetworking

SOAP エラー「Could not connect to host」の完全トラブルシューティングガイド

SOAPless Team9 min read

「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、プロキシに関するトラブルシューティングから解放されます。