サードパーティの依存関係がWordPressの障害を連鎖させる理由
現代のWordPressサイトは、単体で完結して動作することはほぼありません。WooCommerceの決済処理だけを見ても、以下のようにさまざまな外部サービスに依存しています。
- 決済ゲートウェイが支払い処理を担当
- 配送APIがリアルタイムの送料計算を担当
- 税務サービスが税計算やコンプライアンス対応を担当
サイトによっては分析ツール、CRM連携スクリプト、ライブチャットウィジェットなど、多数の外部サービスを読み込んでいます。
問題は、これらサービスのどれか1つでも遅延したり応答しなくなったりすると、その影響が単一の機能だけでは済まないことです。WordPressは外部APIからのレスポンスを待ちながらページ生成を行うため、処理中のPHPスレッドがブロックされ、影響がサイト全体へ波及していきます。
たとえば、決済ゲートウェイの応答が30秒後にタイムアウトすると、その間ずっと1つのスレッドが占有され続け、そのスレッドは他のリクエストを処理することができません。さらに、複数のユーザーが同時にその遅い決済処理に到達すると、複数のスレッドが次々と塞がれ、サイト全体のページ表示速度が低下していきます。共用サーバー環境では、こうしたスレッドを複数サイトで共有しているため、影響はさらに深刻になりやすくなります。
可視性のギャップ:内部要因と外部要因のパフォーマンス問題
そのため、同時に発生するタイムアウトが少数でも、共有プールを簡単に使い切ってしまいます。そうなると外部 APIへのリクエストがタイムアウトし、空きスレッドを待っている他の訪問者にも、502や504などのタイムアウト関連エラーが表示されます。
504エラーの厄介な点は、原因に関係なく同じように見えてしまうことです。こうしたエラーが発生すると、多くの場合、まず CPU 使用率やメモリ消費、インフラ関連の指標を確認します。しかし実際には、根本原因が外部サービス側にあるケースも少なくありません。それでも症状だけを見ると、あたかもサーバー環境に問題があるように見えてしまいます。
サードパーティ障害の影響を最小限に抑えるKinstaのコンテナ化アーキテクチャ
Kinstaでは、すべてのWordPressサイトを独立した隔離コンテナ内で稼働します。これにより、サードパーティサービスで障害が発生した際の影響範囲を限定することができます。
各コンテナには、専用のPHPスレッドプールが割り当てられており、同じプラットフォーム上の他サイトと共有されることはありません。そのため、PHPスレッドが枯渇しても影響はそのコンテナ内にとどまり、他のサイトへ波及することはありません。また、外部APIへのリクエストによってPHPスレッドがすべて使用中になった場合でも、受信リクエストは即座にエラーになるのではなく、NginxとPHP-FPM内部で処理待ちの状態になります。
実際には、共用サーバー環境(いわゆるレンタルサーバー)であれば、全サイトに影響を及ぼしかねない決済ゲートウェイ障害でも、Kinstaでは影響がそのサイトのコンテナ内に限定されます。コンテナ内のスレッドプールには負荷がかかるものの、同一インフラ上の他サイトには一切影響しません。
リクエストタイムアウトの制限により、無期限のブロックを防ぐ
適切に制御されていない場合、PHPスレッドは障害が発生した外部APIへの接続を長時間保持し続ける可能性があります。これに対処するため、Kinstaではmax_execution_timeをデフォルトで300秒に設定し、PHPスクリプトが実行可能な最大時間を制限しています。
さらにブラウザとサーバー間の接続を打ち切り、訪問者に504エラーを返すためのHTTPタイムアウトも別途存在し、Kinstaでは180秒に設定されています。
これらの制限によって、最悪のケースでも訪問者側から見た待機時間には明確な上限が設けられています。ただし、これらの制限だけでは、応答しなくなった外部API呼び出しを確実に停止できるわけではありません。Linux環境では、PHPの実行タイマーはストリーム操作の待機時間をカウントしないためです。
たとえば、決済ゲートウェイの応答待ちでブロックされているスレッドは、PHP側から見ると実行時間がほとんど消費されていないように見えます。そのため、300秒の制限も見た目ほど強力な保護にはなりません。
だからこそ、http_request_timeoutを利用してプラグイン側で明示的にタイムアウトを設定することが、アプリケーションレベルで外部サービスのハングアップを確実に終了させる最も有効な方法になります。
リクエストがタイムアウトすると、占有されていたスレッドは解放され、コンテナは通常数分以内に復旧プロセスへ入ります。
Kinsta APMでサーバーとサードパーティのボトルネックを切り分ける

Kinsta APMの外部画面の使い方

たとえば、決済APIが一覧の上位に表示され、最大応答時間が数秒単位になっている場合、それがパフォーマンス問題の原因である可能性が高いことを示しています。
トランザクションのトレース

トレースを確認することで、どの依存サービスを優先的に最適化または置き換えるべきかを判断できます。たとえば、決済APIへの外部HTTPリクエストが、5.5秒のトランザクション時間のうち5秒を占めていた場合、サーバーインフラ自体は残りの処理をわずか0.5秒で完了していたことになります。
問題が疑われる際にKinsta APMを使用する場合は、次のような流れで調査を進めることができます。
- APMを有効化し、問題が発生している時間帯をカバーする監視期間を選択する
- 問題が現在発生していない場合は、再現を試みる(またはAPMがライブデータを取得するのを待つ)
- データが蓄積されたら「外部」タブを開き、対象の外部リクエストをクリックしてトランザクショントレースを確認し、各スパンの実行時間を調べる
もし外部HTTPリクエストが、トランザクション時間の大部分を占める状態で一覧の上位に表示されていれば、問題の原因を特定するために必要な情報は揃っています。
サードパーティ依存関係を管理するための運用戦略
隔離コンテナ技術によって外部障害の影響範囲は限定できますが、同時に重要なのが、外部サービスをどのように読み込み・呼び出すかです。どれだけ優れたサーバー環境を利用していても、サードパーティ依存関係はアプリケーションレベルで積極的に管理する必要があります。
重要ではないスクリプトを非同期で読み込む
WordPressは、デフォルトではスクリプトを同期的に読み込みます。つまり、<head>内のスクリプトは、ダウンロードと実行が完了するまでブラウザによるページ描画をブロックします。分析スクリプト、ヒートマップツール、マーケティングオートメーション系のコードでは、サードパーティサーバーが遅いだけでページ全体の表示が止まってしまう可能性があります。
ここで重要なのは、外部サーバーが遅延した場合に、同期読み込みと非同期読み込みで挙動が大きく異なる点です。
- 同期(ブロッキング)読み込み:スクリプトのダウンロードと実行が完了するまで、HTML解析が停止。外部サーバーが遅ければ、その間ページ表示も止まる。
- 非同期読み込み:スクリプトをバックグラウンドで読み込みながら、ブラウザはHTML解析とコンテンツ描画を継続。たとえ外部サーバーが遅くても、ページ自体は表示される。
WordPressでは、wp_enqueue_script()を使ってasyncとdeferの両方を標準サポートしています。どちらも、重要ではないスクリプトによるレンダリングブロックを防ぐための仕組みですが、動作には違いがあります。
defer:スクリプトを順番通りに実行するため、依存関係のあるスクリプトに適しているasync:読み込み完了後すぐに実行され、実行順序は保証されない
そのため、asyncは他スクリプトとの依存関係がない単独のトラッキングスクリプトに適しています。
add_action( 'wp_enqueue_scripts', function() {
// Analytics — deferred so it doesn't block the critical path.
wp_enqueue_script(
'google-analytics',
'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX',
[],
null,
[ 'strategy' => 'defer', 'in_footer' => false ]
);
// Marketing script — async because execution order doesn't matter.
wp_enqueue_script(
'hotjar',
'https://static.hotjar.com/c/hotjar-XXXXXX.js',
[],
null,
[ 'strategy' => 'async', 'in_footer' => false ]
);
} );
しかし、決済のような重要な機能に関わるスクリプトは、分析タグやマーケティングスクリプトよりも慎重に読み込む必要があります。決済関連の統合では、決済処理を壊さないために、スクリプトを同期的に実行したり、厳密な実行順序を維持したりする必要がある場合もあります。
つまり、ページ全体に影響を与えずに失敗できる非クリティカルなスクリプトにはasyncやdeferを適用し、ユーザーが取引を完了するために不可欠なスクリプトには適用しない、という考え方が重要です。
外部API呼び出しにタイムアウトを設定
add_filter( 'http_request_timeout', function( $timeout, $url ) {
if ( str_contains( $url, 'api.example.com' ) ) {
return 10; // Don't wait longer than 10 seconds.
}
return $timeout;
}, 10, 2 );
このような上限を設けることで、障害が発生したサービスにPHPスレッドを長時間占有されるのではなく、ユーザーに素早くエラーを返せるようになります。プラグインレベルのタイムアウトをサーバー側の上限よりも十分に短く設定しておけば、1つの遅いAPIが不必要に長い時間スレッドを消費するのを防げます。
フォールバック機構とグレースフルデグラデーション
フォールバックは、外部サービスに障害が発生した際に、エラーを表示する代わりにサイトの機能を維持するための仕組みです。このパターンでは、WordPressのtransientsを使って成功したAPIレスポンスをキャッシュし、ライブ呼び出しが失敗した場合にはキャッシュ済みのデータを返します。
例えば、以下のようになります。
function get_shipping_rates_with_fallback( $package ) {
$cache_key = 'live_shipping_rates_' . md5( serialize( $package ) );
$backup_key = 'backup_shipping_rates_' . md5( serialize( $package ) );
// Return fresh cached rates if they're available.
$cached = get_transient( $cache_key );
if ( $cached !== false ) {
return $cached;
}
// Attempt the live API call with a short timeout.
$response = wp_remote_post( 'https://api.example.com/rates', [
'timeout' => 8,
'body' => [
'destination' => $package['destination'],
'weight' => $package['contents_weight'],
],
] );
// On success: cache the result and update the longer-lived backup.
if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
$rates = json_decode( wp_remote_retrieve_body( $response ), true );
set_transient( $cache_key, $rates, HOUR_IN_SECONDS );
set_transient( $backup_key, $rates, DAY_IN_SECONDS );
return $rates;
}
// On failure: serve stale backup rates rather than an error.
$backup = get_transient( $backup_key );
if ( $backup !== false ) {
return $backup;
}
// No cached data at all: return a flat-rate fallback.
return [
[ 'id' => 'fallback_flat', 'label' => 'Standard Shipping', 'cost' => 9.99 ],
];
}