C
Node.js検証済み

Error: Can't set headers after they are sent to the client の原因と直し方【Dockerで検証済み】

Error: Can't set headers after they are sent to the client の原因と解決方法。検証済みの解決コマンド付きで、現象→原因→解決→確認の順に最短で直せます。

発生したエラー

エラー出力
Error: Can't set headers after they are sent to the client

結論:まずこれで直ります

下の解決コマンドを順に実行すれば直ります。

解決コマンド
cat > /app/server.js << 'EOF'
const http = require('http');

const server = http.createServer((req, res) => {
  // レスポンスを送信する前にすべてのヘッダーをセットする
  res.setHeader('X-Extra', 'value');
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello');
  // res.end() 以降にはヘッダー操作・レスポンス書き込みを行わない
});

server.listen(3000, () => {
  console.log('Server running');
});
EOF

現象どんなエラーか

次の操作を行うと(検証環境: node:20)、上記のエラーが発生します。まずは下の再現コマンドで、同じ状況を再現できることを確認してください。

検証環境:node:20

再現コマンド
mkdir -p /app && cat > /app/server.js << 'EOF'
const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello');
  // レスポンス送信後にさらにヘッダーをセットしようとする(バグ)
  res.setHeader('X-Extra', 'value');
});

server.listen(3000, () => {
  console.log('Server running');
});
EOF
node /app/server.js &
SERVER_PID=$!
sleep 1
curl -s http://localhost:3000/ || true
sleep 1
kill $SERVER_PID 2>/dev/null || true
wait $SERVER_PID 2>/dev/null || true

原因なぜ起きるのか

このエラーは、Node.js の HTTP レスポンスオブジェクトに対して、すでに `res.end()` や `res.write()` でクライアントへデータを送信した後に、`res.setHeader()` や `res.writeHead()` などのヘッダー操作を試みた場合に発生します。HTTP プロトコルの仕様上、ヘッダーはボディより先に送出される必要があり、一度レスポンスのストリームが閉じられると後からヘッダーを変更することは不可能です。Node.js はこの違反を検出して ERR_HTTP_HEADERS_SENT エラーをスローします。修正方法は、`res.end()` を呼び出す前にすべてのヘッダー設定(`res.setHeader()`、`res.writeHead()` 等)を完了させることです。また、コールバック内で条件分岐がある場合は、複数の `res.end()` が呼ばれないよう `return` を使って早期リターンすることも重要です。

解決解決手順

修正コマンド
cat > /app/server.js << 'EOF'
const http = require('http');

const server = http.createServer((req, res) => {
  // レスポンスを送信する前にすべてのヘッダーをセットする
  res.setHeader('X-Extra', 'value');
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello');
  // res.end() 以降にはヘッダー操作・レスポンス書き込みを行わない
});

server.listen(3000, () => {
  console.log('Server running');
});
EOF

確認直ったか確認する

確認コマンド
node /app/server.js &
SERVER_PID=$!
sleep 1
RESPONSE=$(curl -si http://localhost:3000/)
kill $SERVER_PID 2>/dev/null || true
wait $SERVER_PID 2>/dev/null || true
echo "$RESPONSE" | grep -q 'X-Extra: value' && echo "Header present" && exit 0
exit 1

動画で見る

この記事の解決手順は実環境で検証しています

山田 英紀(社内SE 5年以上・13業種以上の業務システムを開発/運用)が、 掲載コマンドを検証環境で実行し、再現〜解決〜確認まで通ることを確認しています。

この手順でも直らない・自社の環境で再現する場合

エラー調査・修正から、業務システムの改善までご相談いただけます。

無料で相談する

関連する記事