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

SSE 스트리밍이 nginx 뒤에서는 실시간으로 안 오고 응답 끝날 때 한꺼번에 옵니다

s
sse_yerin
조회 416댓글 1

LLM 스트리밍 응답을 SSE(text/event-stream)로 프론트에 흘려보내고 있습니다. 로컬에서 서버에 직접 붙으면 토큰이 실시간으로 한 조각씩 잘 오는데, nginx 리버스 프록시를 거치는 스테이징에 올리니까 스트리밍이 안 됩니다. 연결이 다 끝난 다음에 전체 텍스트가 한 번에 툭 떨어져요.

백엔드는 Content-Type을 text/event-stream 으로 제대로 내보내고 있고, 청크마다 flush도 하고 있습니다. 로컬 직접 접속에선 확실히 실시간으로 동작하는 걸 봤어요. 그러니 앱 코드보다는 nginx 설정 문제인 것 같은데, 어디를 봐야 할지 모르겠습니다.

댓글 1

김김영삼2개월 전
nginx의 proxy_buffering 때문입니다. 이게 기본으로 켜져 있어서, 업스트림(백엔드) 응답을 버퍼에 차곡차곡 모았다가 어느 정도 차거나 연결이 끝나면 한 번에 클라이언트로 내보냅니다. 일반 응답엔 유리한 최적화인데 SSE 같은 스트리밍한텐 치명적이에요. 백엔드가 아무리 flush 해도 nginx가 중간에서 붙잡고 있으니 실시간성이 죽는 거죠.

SSE를 서빙하는 location 블록에 이걸 넣으세요.

proxy_buffering off;
proxy_cache off;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_read_timeout 3600s;

proxy_buffering off 가 핵심이고, proxy_read_timeout을 길게 잡는 건 SSE 연결이 오래 열려 있으니 기본 60초에서 안 끊기게 하려는 거예요. Connection 헤더를 비우고 http_version 1.1로 두는 건 keepalive 유지용입니다.

location 블록을 건드리기 싫거나 SSE 엔드포인트만 콕 집어서 끄고 싶으면, 백엔드 응답에 X-Accel-Buffering: no 헤더를 실어 보내세요. nginx가 이 헤더를 보면 그 응답만 버퍼링을 끕니다. 코드 한 줄로 되니 이게 더 깔끔할 때도 많아요.

마지막으로 함정 하나. gzip이 켜져 있으면 압축 버퍼 때문에 또 뭉쳐서 나갑니다. event-stream엔 gzip 걸지 마세요. 이 세 개(proxy_buffering, gzip, read_timeout) 잡으면 로컬처럼 실시간으로 흐를 겁니다.
Ctrl+Enter로 등록