본문 바로가기
Q&A해결2026년 7월 19일1분 읽기

"Cannot set headers after they are sent to the client" 에러가 간헐적으로만 떠요

n
node_seunggi
조회 897댓글 2

운영 로그에 하루에 몇 번씩 이 에러가 찍힙니다. 항상 나는 게 아니라 특정 요청에서 가끔만 나서 재현이 정말 안 돼요.

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (node:_http_outgoing:...)
    at ServerResponse.header (.../express/lib/response.js:...)

핸들러 안에서 조건 분기해서 res.json을 여러 번 부를 일은 없다고 생각했는데, 문제 라우트를 아무리 봐도 res를 두 번 보내는 데를 못 찾겠습니다. 어떤 요청은 되고 어떤 요청은 안 되고... 대체 뭘 봐야 할까요?

댓글 2

김김영삼3개월 전
이 에러는 100% res를 두 번 보내고 있다는 뜻이에요. 응답이 한 번 나가면 헤더가 확정돼서, 그 다음에 또 뭔가 쓰려고 하면 터집니다. 간헐적으로만 나는 건 대부분 async 흐름에서 return을 빠뜨려 생기는 경쟁이에요. 흔한 패턴 몇 개를 짚을게요.

가장 많은 건 res.json 뒤에 return을 안 붙인 early-return입니다. if (!user) { res.status(404).json(...) } 하고 return을 안 하면 아래 정상 응답 코드까지 그대로 실행돼서 두 번 보내집니다. 순수 동기라면 항상 터지겠지만 중간에 await가 끼면 타이밍 따라 가끔만 터지죠. if 블록마다 return res.json(...) 형태로 바꾸세요.

두 번째는 정상 경로와 catch가 겹치는 경우예요. try 안에서 res.json으로 응답을 이미 보냈는데 그 아래 코드가 throw해서 catch로 가고, catch에서 또 res.status(500)을 보내는 식입니다. 응답을 보낸 뒤의 코드는 catch에 안 걸리게 구조를 잡아야 해요.

세 번째는 요청 타임아웃이나 클라이언트 abort 핸들러에서 res를 보내는데 정상 응답과 겹치는 경우고요.

디버깅은 이렇게 하세요. 문제 라우트에서 res.json 부르기 직전에 if (res.headersSent) { console.trace('double send'); return } 를 넣으면 두 번째로 보내려는 지점의 스택이 그대로 찍힙니다. 그 한 방이면 범인이 바로 나와요. 그리고 앞으로 응답은 습관적으로 return res.xxx() 로 쓰는 걸 권합니다.
nnode_seunggi(guest)3개월 전
res.headersSent에 console.trace 걸었더니 바로 잡혔어요. 404 early return에서 return 하나 빠진 거였습니다... 정확히 그거였네요 😅 감사합니다.
Ctrl+Enter로 등록