Strapi + ALB構成における502エラーの真因とタイムアウト設定のベストプラクティス
StrapiとALB環境で頻発する502エラー。その原因はrequestTimeoutではなく、ALBの接続再利用とNode.jsの短いkeepAliveTimeoutの不整合でした。根本原因の解説と、推奨される設定方法をまとめます。
TL;DR
- Strapi + ALB構成で断続的に発生する502エラーの主な原因は、ALBの接続再利用と、Node.jsの
keepAliveTimeout(デフォルト5秒)の不整合。 - ALBがアイドル状態のコネクションを再利用しようとした際、Node.js側が先に接続を切ってしまっているために
502 Bad Gatewayが発生する。 - 解決策は、
bootstrap関数でkeepAliveTimeoutをALBのアイドルタイムアウトより長く設定すること。これが最も直接的で効果的な対策となる。
よくある誤解と問題の背景
- インフラ構成: ALB → ECS Fargate (Strapi/Node.js)
- 事象: アプリケーションログにエラーはないが、ALBのメトリクスで502エラーが多発。
この問題に直面した際、「サーバーのrequestTimeoutがALBのアイドルタイムアウトより短いからだ」と考えがちですが、これは必ずしも正しくありません。Node.jsのrequestTimeoutはデフォルトで5分と長く、多くの場合、真犯人は別にいます。
原因の厳密な解説
ALBとNode.jsサーバー間のタイムアウト設定の不整合、特にKeep-Aliveの扱いに問題の核心があります。
- ALBの挙動: アイドルタイムアウト(デフォルト60秒)内であれば、パフォーマンス向上のために確立済みのTCPコネクションを再利用しようとします。
- Node.jsの挙動:
keepAliveTimeoutのデフォルト値は5秒です。リクエスト完了後、次のリクエストが来ないままこの時間を超えると、Node.jsサーバーは一方的にコネクションを閉じます。
この仕様の差が、以下の順で502エラーを引き起こします。
- ALBがクライアントリクエストを受け、Strapiへ転送。処理は正常に完了します。
- ALBは効率化のため、そのコネクションをプールし、次のリクエストに備えます(アイドルタイムアウト60秒以内)。
- しかし、Strapi(Node.js)側では
keepAliveTimeoutの5秒が経過したため、そのコネクションを閉じてしまいます (FINパケットを送信)。 - ALBは、既に閉じられたとは知らずに、そのコネクションを再利用して次のリクエストを送信。
- ターゲットから応答がないため、ALBは
502 Bad Gatewayエラーをクライアントに返します。
これが、断続的に502エラーが発生するメカニズムです。
解決策
意図しないコネクション切断を防ぐため、Strapi(Node.js)側のタイムアウト値をALBより長く設定します。
1. bootstrap関数でkeepAliveTimeoutを調整する (最も推奨)
問題の直接的な原因であるkeepAliveTimeoutを調整するのがベストプラクティスです。Strapiのsrc/index.jsにあるbootstrap関数で、サーバーインスタンスに直接設定します。
'use strict';
module.exports = {
// ...
bootstrap({ strapi }) {
// 注意: Strapiのバージョンや起動タイミングによっては、bootstrap時点で
// strapi.serverオブジェクトが存在しない場合があります。
// if文による存在チェックを強く推奨します。
if (strapi.server && strapi.server.httpServer) {
const albIdleTimeout = 60 * 1000; // ご自身のALBのアイドルタイムアウト値に合わせる
// ALBのタイムアウトより少し長く設定
strapi.server.httpServer.keepAliveTimeout = albIdleTimeout + 5 * 1000; // 例: 65秒
strapi.server.httpServer.headersTimeout = albIdleTimeout + 10 * 1000; // 例: 70秒
}
},
};
2. (補助的) config/server.jsでrequestTimeoutを設定する
もしDBの重いクエリなど、リクエスト処理自体に時間がかかるケースも考慮するなら、requestTimeoutも合わせて設定しておくとより堅牢です。
注意: Strapi公式ドキュメントが推奨する正しい記法で設定してください。
module.exports = ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
app: {
keys: env.array('APP_KEYS'),
},
http: {
serverOptions: {
// ALBのアイドルタイムアウトより長く設定
requestTimeout: 65 * 1000,
},
},
});
この設定をデプロイ後、502エラーは完全に解消されました。
まとめと次のステップ
ALB + Node.js環境での502エラーは、多くの場合keepAliveTimeoutの不整合が原因です。requestTimeoutを闇雲に伸ばすのではなく、まずはkeepAliveTimeoutをALBの設定に合わせて調整しましょう。
今回の対応はインフラレイヤーでの設定見直しであり、もしAPIの応答自体が遅い場合は、別途パフォーマンス改善が必要です。APMツールなどでボトルネックを特定し、クエリ改善やインデックスの見直しといった次のアクションに進むことをお勧めします。