MeWrite Docs

Node.js: Cannot set headers after they are sent to the client

Express/Node.jsで1リクエストに複数回レスポンスを送ると発生するERR_HTTP_HEADERS_SENTの原因と解決策

概要

Cannot set headers after they are sent to the client は、1つのHTTPリクエストに対してレスポンスを2回以上送信しようとしたときに発生します。HTTPは1リクエストにつき1レスポンスのみで、ヘッダー送信後は変更できないため、res.json() / res.send() / res.end() を重複して呼ぶとこのエラーになります。

エラーメッセージ

現行のNode.js(24 LTS / 22 LTS)およびExpress 5では、ERR_HTTP_HEADERS_SENT コード付きで出力されます。

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (node:_http_outgoing:699:11)
    at ServerResponse.header (/app/node_modules/express/lib/response.js:771:10)
    at ServerResponse.send (/app/node_modules/express/lib/response.js:170:12)
    at /app/routes/user.js:14:9

旧バージョンでは次の形式で出ることもあります(同じ原因です)。

Error: Can't set headers after they are sent.

原因

  1. レスポンス送信後の return 忘れ: res.json() の後に処理が続き、もう一度 res.send() が呼ばれる(最頻パターン)。
  2. 非同期処理の二重発火: コールバックと await の両方、あるいは Promise の thencatch の両方でレスポンスを送ってしまう。
  3. ミドルウェアでの送信後に next(): ミドルウェアがレスポンスを返したのに next() を呼び、後続ハンドラが再送信する。
  4. ループ内での送信: forEach / for の各要素で res.send() を呼んでいる。

解決策

1. レスポンスを送ったら必ず return で打ち切る

1
2
3
4
5
6
7
app.get("/user/:id", async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) {
    return res.status(404).json({ error: "Not found" }); // returnで処理を止める
  }
  res.json(user); // ここは user が存在する場合のみ実行される
});

return がないと 404 を返した後も res.json(user) に到達し、二重送信になります。

2. res.headersSent で送信済みかチェックする

共通のエラーハンドラなど、複数経路から呼ばれる関数では送信済みフラグを確認します。

1
2
3
4
function sendError(res, err) {
  if (res.headersSent) return; // すでに送信済みなら何もしない
  res.status(500).json({ error: err.message });
}

3. 非同期エラーはハンドラ内で完結させる(Express 5)

Express 5 では async ハンドラが reject すると自動的にエラーハンドラへ転送されます。try/catch で握りつぶして二重に送らないようにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Express 5: async関数のthrow/rejectは自動でエラーハンドラに渡る
app.get("/data", async (req, res) => {
  const data = await fetchData(); // throwされてもここで二重送信は起きない
  res.json(data);
});

// 集約エラーハンドラ(引数4つ)
app.use((err, req, res, next) => {
  if (res.headersSent) return next(err);
  res.status(500).json({ error: err.message });
});

4. ミドルウェアでは「送信」か「next()」のどちらか一方だけ

1
2
3
4
5
6
function authGuard(req, res, next) {
  if (!req.headers.authorization) {
    return res.status(401).json({ error: "Unauthorized" }); // 送信したらnext()は呼ばない
  }
  next(); // 認証OKのときだけ後続へ
}

よくある間違い

ループ内で各要素ごとにレスポンスを送ると、2件目以降でエラーになります。

1
2
3
4
5
// NG: 各itemでres.sendが呼ばれる
items.forEach((item) => res.send(item));

// OK: 配列をまとめて1回で返す
res.json(items);

関連エラー

関連エラー

Node.js の他のエラー

最終更新: 2026-06-05