본문 바로가기
Nginx

Nginx 502 'upstream sent too big header' - proxy_buffer_size, proxy_buffers, busy_buffers_size로 큰 응답 헤더 받기

aappsroot·2026년 9월 7일·조회 1

1. 개요

백엔드 WAS 앞에 Nginx를 리버스 프록시로 두는 구성은 흔하다. 평소 잘 돌던 경로가 어느 날 특정 사용자, 특정 API에서만 502를 내뱉을 때가 있다. 로그인 직후나 세션이 커진 계정에서만 재현되는 식이다. 나도 이 증상을 처음 만났을 때는 백엔드가 죽은 줄 알고 애플리케이션 로그부터 뒤졌는데, 정작 원인은 Nginx가 응답 헤더를 담을 버퍼가 모자랐던 것이었다.

결론부터 말하자면, 이 502는 업스트림이 보낸 응답 헤더가 proxy_buffer_size 한 장에 안 들어가서 생긴다. 해결은 proxy_buffer_size를 키우는 것이지만, 이 값만 단독으로 올리면 proxy_busy_buffers_size 자동 계산값이 상한을 넘어 nginx -t가 거부한다. 그래서 proxy_buffer_size, proxy_buffers, proxy_busy_buffers_size 세 지시어를 함께 맞춰야 한다.

대상 버전은 Nginx 1.24 이상(1.26/1.27 포함)이며, 아래 기본값과 계산 규칙은 공식 ngx_http_proxy_module 문서 기준이다. 값은 플랫폼 메모리 페이지 크기에 따라 다를 수 있는데, 이 글은 가장 흔한 x86-64 리눅스(페이지 4KB)를 기준으로 적는다.

2. 증상: 502와 error.log 한 줄

클라이언트는 그냥 502 Bad Gateway를 받는다. 원인은 error.log에 정확히 찍힌다.

2026/09/07 13:22:10 [error] 1123#1123: *45 upstream sent too big header while
reading response header from upstream, client: 203.0.113.10, server: sarc.io,
request: "GET /api/me HTTP/1.1", upstream: "http://127.0.0.1:3000/api/me",
host: "sarc.io"

핵심 단서는 reading response header from upstream이다. 응답 본문이 아니라 응답 헤더가 문제라는 뜻이다. Nginx는 업스트림에서 온 상태줄과 헤더 전체를 첫 버퍼 한 장에 읽어들인다. 이 첫 버퍼의 크기가 proxy_buffer_size다. 헤더 덩어리가 그 한 장을 넘으면 Nginx는 더 읽지 못하고 502로 끊는다.

어떤 응답이 헤더를 키우는지 감을 잡으려면 백엔드 응답 헤더를 직접 재보면 된다. Nginx를 거치지 않고 업스트림에 바로 붙어 확인한다.

$ curl -s -D - -o /dev/null http://127.0.0.1:3000/api/me \
    -H 'Cookie: session=...(실제 세션)...' | wc -c
9241

여기서 나오는 헤더 바이트가 기본 proxy_buffer_size(4KB)를 넘으면 502가 난다. 범인은 대개 큼직한 Set-Cookie, JWT를 통째로 쿠키에 담는 구성, 여러 개로 쪼개진 세션 쿠키, 길게 붙는 WWW-Authenticate 같은 헤더다.

2-1. 헤더 버퍼란 무엇인가

Nginx가 업스트림 응답을 버퍼링할 때(proxy_buffering on, 기본값), 응답은 두 부분으로 나뉘어 처리된다. 하나는 상태줄과 헤더 블록이고, 다른 하나는 본문이다. 상태줄과 헤더는 반드시 버퍼 한 장에 통째로 들어가야 한다. 여러 장에 걸쳐 이어 담지 않는다. 그 한 장의 크기가 proxy_buffer_size이고, 이 글의 502는 바로 이 한 장이 모자랄 때 나는 에러다.

참고로 이건 클라이언트가 보낸 요청 헤더를 담는 large_client_header_buffers와는 다른 지시어다. 큰 쿠키는 요청 방향과 응답 방향 양쪽을 다 건드릴 수 있는데, 요청 헤더가 너무 크면 Nginx가 400(또는 431)을 내고, 업스트림이 내려주는 응답 헤더가 크면 지금 다루는 502가 난다. 이 글은 응답 방향, 즉 proxy_buffer_size 쪽이다.

3. 세 지시어와 기본값

응답 헤더 버퍼링에 관여하는 지시어는 세 개다. 하나씩 한 줄 정의로 끊어 본다.

proxy_buffer_size - 업스트림 응답의 첫 부분(상태줄 + 헤더)을 읽는 버퍼 한 장의 크기. 502를 직접 좌우하는 값이다.

proxy_buffers - 응답 본문을 버퍼링하는 버퍼의 개수와 각 장의 크기.

proxy_busy_buffers_size - 아직 응답을 다 읽지 못한 상태에서, 이미 클라이언트로 내보내는 데 쓰이는 버퍼의 총량 상한.

x86-64 리눅스에서의 기본값은 다음과 같다.

proxy_buffer_size        4k;
proxy_buffers            8 4k;
proxy_busy_buffers_size  8k;   # 지정하지 않으면 자동 계산됨

여기서 중요한 규칙이 proxy_busy_buffers_size의 자동 계산과 검증 조건이다. 공식 문서와 소스 기준으로 세 가지를 기억하면 된다.

  • 기본값(자동): proxy_busy_buffers_size를 명시하지 않으면 2 × max(proxy_buffer_size, proxy_buffers 한 장 크기)로 계산된다.
  • 하한: max(proxy_buffer_size, proxy_buffers 한 장 크기) 이상이어야 한다.
  • 상한: proxy_buffers 전체에서 한 장을 뺀 크기, 즉 (개수 - 1) × 한 장 크기 미만이어야 한다.

이 세 규칙이 다음 절의 함정을 만든다.

4. proxy_buffer_size만 올리면 nginx -t가 거부하는 이유

502를 없애려면 proxy_buffer_size를 키우는 게 맞다. 그래서 이것만 손대고 싶어진다.

location /api/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_buffer_size 16k;    # 이거 하나만 올림
}

이 상태로 문법 검사를 돌리면 표준 리눅스(페이지 4KB, 기본 proxy_buffers 8 4k)에서는 통과하지 못한다.

$ sudo nginx -t
nginx: [emerg] "proxy_busy_buffers_size" must be less than the size of all
"proxy_buffers" minus one buffer in /etc/nginx/conf.d/sarc.conf:14
nginx: configuration file /etc/nginx/nginx.conf test failed

계산을 따라가 보면 이유가 분명하다. proxy_busy_buffers_size를 명시하지 않았으니 Nginx가 자동으로 2 × max(16k, 4k) = 32k로 잡는다. 그런데 proxy_buffers는 기본값 8 4k 그대로라 상한이 (8 - 1) × 4k = 28k다. 32k는 28k를 넘으므로 즉시 emerg로 거부된다.

여기서 한 번 걸렸다. proxy_buffer_size만 올려도 502가 사라진다는 식의 설명을 그대로 따라 했다가 reload 자체가 막히는 것이다. proxy_buffer_size를 키우면 proxy_buffersproxy_busy_buffers_size도 같이 손봐야 한다.

5. 올바른 설정

5-1. 세 지시어를 함께 맞춘 최소 처방

nginx -t를 통과하면서 큰 응답 헤더를 받는 최소 구성은 세 값을 일관되게 올리는 것이다.

location /api/ {
    proxy_pass http://127.0.0.1:3000;

    proxy_buffer_size       16k;   # 헤더 한 장을 키움
    proxy_buffers           8 16k; # 본문 버퍼도 같은 장 크기로
    proxy_busy_buffers_size 32k;   # 명시적으로 지정
}

이 값들이 규칙을 만족하는지 검산해 보자. busy의 하한은 max(16k, 16k) = 16k, 상한은 (8 - 1) × 16k = 112k다. 지정한 32k는 16k 이상이고 112k 미만이므로 조건 안에 든다. 문법 검사를 돌려 확인한다.

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration test is successful
$ sudo systemctl reload nginx

핵심은 502를 없애는 값이 proxy_buffer_size라는 점이다. 응답 헤더 덩어리가 이 한 장(여기서는 16k) 안에만 들어오면 502는 사라진다. proxy_buffersproxy_busy_buffers_size는 검증을 통과시키기 위해 함께 맞춰 주는 값이다.

5-2. 헤더가 더 클 때

2절의 curl로 잰 헤더가 16k도 넘는다면(예: JWT 여러 개가 쿠키로 쪼개져 내려오는 구성), 한 장을 더 키운다. 예를 들어 32k까지 잡으려면 다음과 같이 한다.

    proxy_buffer_size       32k;
    proxy_buffers           8 32k;
    proxy_busy_buffers_size 64k;

검산: busy 하한 max(32k, 32k) = 32k, 상한 (8 - 1) × 32k = 224k. 지정한 64k가 범위 안이다. proxy_buffer_size는 실제로 측정한 헤더 크기보다 한 단계 여유를 두고 잡는 편이 안전하다. 응답 헤더 크기는 세션 상태나 쿠키 개수에 따라 늘어날 수 있기 때문이다.

5-3. 어디에 두는가

이 세 지시어는 http, server, location 블록에 둘 수 있다. 헤더가 큰 특정 경로만 문제라면 그 location에만 두는 게 영향 범위가 좁아 낫다. 프록시하는 백엔드 전체가 큰 헤더를 쓴다면 server 블록에 한 번만 적어 상속시킨다.

6. 검증

reload 후 문제의 경로를 다시 때려 상태 코드를 확인한다.

$ curl -s -o /dev/null -w '%{http_code}\n' https://sarc.io/api/me \
    -H 'Cookie: session=...(실제 세션)...'
200

502가 200으로 바뀌고 error.logtoo big header가 더는 찍히지 않으면 해결된 것이다. 재발 감시는 로그를 한 줄로 거는 게 편하다.

$ sudo tail -f /var/log/nginx/error.log | grep --line-buffered 'too big header'

7. 함정 정리

  • proxy_buffer_size만 올리지 마라. 표준 리눅스에서 proxy_busy_buffers_size 자동값이 상한을 넘어 nginx -t가 emerg로 거부한다. 세 값을 함께 맞춘다.
  • busy 범위를 검산하라. 하한은 max(proxy_buffer_size, proxy_buffers 한 장), 상한은 (proxy_buffers 개수 - 1) × 한 장 크기다. 명시적으로 지정하면 자동 계산에 휘둘리지 않는다.
  • 본문이 아니라 헤더 문제다. 에러 문구의 reading response header를 놓치지 마라. 본문 버퍼링을 아무리 키워도 502는 안 사라진다.
  • 요청 헤더 400과 헷갈리지 마라. 큰 쿠키가 요청 방향에서 400/431을 낸다면 그건 large_client_header_buffers 영역이다.

정리하면, 이 502는 업스트림 응답 헤더가 proxy_buffer_size 한 장을 넘어서 생긴다. curl로 헤더 크기를 재서 그보다 여유 있게 proxy_buffer_size를 잡고, proxy_buffers의 장 크기를 같게 올린 뒤, proxy_busy_buffers_size를 범위 안 값으로 명시하면 된다.

자주 묻는 질문

왜 proxy_buffer_size만 올리면 nginx -t가 실패하나?

proxy_busy_buffers_size를 지정하지 않으면 Nginx가 2 × max(proxy_buffer_size, proxy_buffers 한 장)으로 자동 계산한다. 표준 리눅스 기본값(proxy_buffers 8 4k)에서 proxy_buffer_size만 16k로 올리면 자동값이 32k가 되는데, 상한인 (8-1)×4k=28k를 넘어 nginx -t가 emerg로 거부한다. proxy_buffers와 proxy_busy_buffers_size를 함께 맞춰야 한다.

'upstream sent too big header'는 응답 헤더 문제인가 본문 문제인가?

응답 헤더 문제다. 에러 문구의 'reading response header from upstream'이 핵심이다. Nginx는 업스트림 응답의 상태줄과 헤더 전체를 proxy_buffer_size 버퍼 한 장에 읽어들이는데, 그 한 장을 넘으면 502로 끊는다. 본문 버퍼링(proxy_buffers)을 키워도 이 502는 사라지지 않는다.

proxy_buffer_size를 얼마로 잡아야 하나?

curl -s -D - -o /dev/null <업스트림 URL> | wc -c 로 실제 응답 헤더 바이트를 잰 뒤, 그보다 한 단계 여유를 둔 값을 잡는다. 세션 쿠키가 큰 구성이면 16k, JWT가 여러 쿠키로 쪼개져 내려오면 32k처럼 측정값에 맞춰 올린다. 응답 헤더 크기는 세션 상태에 따라 달라질 수 있으므로 딱 맞추기보다 여유를 둔다.

proxy_busy_buffers_size의 허용 범위는 어떻게 되나?

하한은 max(proxy_buffer_size, proxy_buffers 한 장 크기), 상한은 proxy_buffers 전체에서 한 장을 뺀 크기 즉 (개수-1)×한 장 크기다. 예를 들어 proxy_buffers 8 16k라면 busy는 16k 이상 112k 미만이어야 한다. 자동 계산에 맡기지 말고 이 범위 안 값으로 직접 지정하는 편이 예측 가능하다.

요청 헤더가 커서 나는 400이나 431과는 무슨 차이인가?

방향이 다르다. 큰 쿠키가 클라이언트에서 Nginx로 가는 요청 헤더를 키우면 large_client_header_buffers 한도를 넘어 400 또는 431이 난다. 반대로 업스트림이 Set-Cookie 등으로 내려주는 응답 헤더가 크면 proxy_buffer_size 한도를 넘어 502가 난다. 이 글의 502는 응답 방향, 즉 proxy_buffer_size 쪽이다.

관련 글

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.