Node.jsのHTTPSリクエストにおける「Error: socket hang up」の解決方法

intermediate💚 Node.js2026-04-11| Node.js (v12+), Axios, node-fetch、または Linux/macOS/Windows 上のネイティブ https モジュール

Error Message

Error: socket hang up
#nodejs#https#axios#デバッグ#ネットワーク

「Socket Hang Up」の正体とは?Error: socket hang upほど苛立たしいものはありません。これはネットワークの世界で、誰かが話の途中で電話を切ってしまうようなものです。Node.jsアプリケーションは接続の確立には成功しましたが、レスポンスが完了する前に、サーバーまたはロードバランサーのような中間要素によって接続が切断されました。

技術的には、これは通常 ECONNRESET として現れます。ETIMEDOUT はサーバーに到達すらできなかったことを意味しますが、hang up(ハングアップ)は通信中に回線が切れたことを意味します。これは多くの場合、タイムアウト設定の不一致や、接続プールの枯渇が原因で発生します。

Error: socket hang up
    at connResetException (internal/errors.js:607:14)
    at TLSSocket.socketOnEnd (_http_client.js:493:23)
    at TLSSocket.emit (events.js:327:22)
    at endReadableNT (internal/streams/readable.js:1327:12)
    at processTicksAndRejections (internal/process/task_queues.js:80:21) {
  code: 'ECONNRESET'
}

原因を突き止めるコードを書き直す前に、接続がどこで切れているのかを特定する必要があります。自分のコードなのか、ネットワークなのか、それともリモートサーバーなのか?

  • サーバー側のログを確認する: APIサーバーがクラッシュしていませんか?ハングアップが発生した正確なタイムスタンプで、メモリの急増や500系のエラーがないか探します。- ネットワークパスを監査する: 企業のVPN内やAWSのApplication Load Balancer (ALB)を使用している場合、これらの中間要素には通常60秒のアイドルタイムアウトがあります。リクエストに61秒かかると、ALBはソケットを強制終了します。- 詳細なトレースを実行する: curl -v https://api.example.com を実行します。これにより、サーバーが User-Agent などの特定のヘッダーを期待しているか(Nodeスクリプトがそれをスキップしていないか)を確認できます。- Node.jsのデバッグを有効にする: NODE_DEBUG=http,https,net node app.js を使用してアプリを起動します。低レベルのハンドシェイクや、FIN パケットがいつ受信されたかを正確に把握できます。## 実証済みの解決策### 1. 永続的なHTTPエージェントを使用するv19より前のNode.jsバージョンでは、デフォルトで Keep-Alive が有効になっていません。これは、リクエストごとに新しいTCP接続を開閉することを意味し、低速で失敗しやすくなります。永続的なエージェントを介して接続を再利用することが、このエラーに対する最も効果的な解決策です。 ネイティブの https モジュールの場合:
const https = require('https');

const agent = new https.Agent({
  keepAlive: true,
  keepAliveMsecs: 1000, // 1秒ごとにTCP keep-aliveプローブを送信
  timeout: 60000        // 標準的な60秒のサーバーアイドルタイムアウトに合わせる
});

const options = {
  hostname: 'api.example.com',
  path: '/large-dataset',
  agent: agent 
};

const req = https.request(options, (res) => { /* レスポンスを処理 */ });
req.on('error', (e) => console.error(e));
req.end();

Axiosユーザーの場合:

const axios = require('axios');
const https = require('https');

const instance = axios.create({
  // Keep-Aliveはハンドシェイクのオーバーヘッドを削減し、突然の切断を防ぐ
  httpsAgent: new https.Agent({ keepAlive: true }),
});

instance.get('https://api.example.com/data')
  .then(res => console.log(res.data))
  .catch(err => console.error(err.message));

2. タイムアウト設定を合わせるサーバーが重いデータベースクエリの処理に30秒かかるのに、Node.jsのソケットがそれより短い時間に設定されている場合、クライアントは接続を切断します。ローカルのタイムアウト設定を、サーバーの最大処理時間よりもわずかに長く設定する必要があります。

const req = https.request(options, (res) => { /* ... */ });

// 遅いレポートのためにソケットのタイムアウトを2分に延長
req.setTimeout(120000, () => {
  console.warn('ソケットが120秒間アイドル状態です。手動で中断します。');
  req.destroy();
});

3. POSTリクエストのContent-Lengthを計算する多くの現代的なファイアウォールやNginxの設定では、Content-Length ヘッダーがない、または正しくないPOSTリクエストを即座に遮断します。これはリクエスト・スマグリングを防ぐためのセキュリティ対策です。Nodeにサイズの推測を任せず、明示的に計算してください。

const data = JSON.stringify({ user: 'jsmith', action: 'sync' });
const options = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Content-Length': Buffer.byteLength(data),
    'User-Agent': 'MyApp/1.0.0'
  }
};

修正を確認する方法永続的なエージェントの実装やタイムアウトの調整が終わったら、接続のテストを行います。

  • ループテスト: 50回連続でリクエストを実行します。問題がソケットの枯渇であった場合、以前は最初の10回程度しか成功しなかったかもしれませんが、今回は50回すべて成功するはずです。- アクティブハンドルの監視: 開発環境で process._getActiveHandles() を使用します。これにより、ソケットが再利用されており、「無限に」開いたままリークしていないことを確認できます。- プロキシログの確認: Nginxを使用している場合は、upstream prematurely closed connection を探します。このエラーがNginxのログから消えていれば、Node.jsのエージェント設定が正しく機能しています。## まとめ- Keep-Aliveは必須: トラフィックの多いアプリでは、必ずエージェントを使用して接続の永続性を管理してください。- エラーを適切に処理する: 必ず .on('error') リスナーを付加してください。これがないと、単純なソケットのハングアップが uncaughtException を引き起こし、プロセス全体がクラッシュする可能性があります。- 中間要素に注意: コードが完璧であっても、バックエンドが遅い場合、タイムアウトの短いAWSロードバランサーやCloudflareワーカーによって接続が切断されることがあります。

Related Error Notes