Nginx 502 error 디버깅

문제

개발기 서버로 사용하는 on-premise 서버에서 application을 배포하고
Nginx의 .conf 파일을 통해서 URL을 정해주고 있는데
기존 Next.js application을 잘 뜨는데
신규 Next.js application은 browser에서 502 에러가 났다.
이것저것 해본후 원인은 header size의 문제였다.

Header size

현재는 Nginx의 proxy버퍼 사이즈를 늘리고 Nginx 문제를 해결한 후지만
새로 배포한 사이트의 헤더 사이즈를 한 번 보자.

에러가 났던 사이트

curl -I -s -w '%{size_header}\n' https://new.example.com

 

결과

HTTP/1.1 200 OK
Date: Wed, 26 Mar 2025 08:49:06 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Content-Security-Policy: default-src 'self';  script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: repo.whatap-browser-agent.io/rum/prod/ static.nid.naver.com developers.kakao.com  appleid.cdn-apple.com t1.daumcdn.net t1.kakaocdn.net stdpay.inicis.com stdux.inicis.com nice.checkplus.co.kr umami.example.com js.tosspayments.com;  connect-src 'self' rum-ap-northeast-2.whatap-browser-agent.io nice.checkplus.co.kr umami.example.com event.tosspayments.com apigw-sandbox.tosspayments.com apigw.tosspayments.com google-analytics.com overbridgenet.com;  style-src 'self' 'unsafe-inline' stdpay.inicis.com;  font-src 'self' data:;  worker-src 'self' blob:;  img-src 'self' blob: data: img-stg.example.com;  media-src 'self' blob: data: commondatastorage.googleapis.com img-stg.example.com;  frame-src 'self' postcode.map.daum.net stdpay.inicis.com nice.checkplus.co.kr payment-gateway-sandbox.tosspayments.com api.tosspayments.com pay-sandbox.tosspayments.com img-stg.example.com;  frame-ancestors 'self' bo-dev.example.com po-dev.example.com cc-dev.example.com bo-mui-dev.example.com po-mui-dev.example.com cc-mui-dev.example.com;  form-action 'self' stdpay.inicis.com nice.checkplus.co.kr mobile.inicis.com inilite.inicis.com  sharer.kakao.com accounts.kakao.com;
Timing-Allow-Origin: *
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: on
Strict-Transport-Security: max-age=31536000; includeSubDomains
Permissions-Policy: camera=(), microphone=(), geolocation=()
set-cookie: NEXT_LOCALE=ko; Path=/
set-cookie: lang_cd=ko; Path=/; Expires=Thu, 27 Mar 2025 08:49:05 GMT; Secure
set-cookie: site_no=1; Path=/; Expires=Thu, 27 Mar 2025 08:49:05 GMT; Secure
set-cookie: mall_no=10001; Path=/; Expires=Thu, 27 Mar 2025 08:49:05 GMT; Secure
x-middleware-rewrite: /ko
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
link: </_next/static/media/119cf01b445a4dc3-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/12f0acdcae926a24-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/e00e15f44d7b58c0-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </images/assets/message.png>; rel=preload; as="image", </_next/static/css/94c33e1b57090fc5.css>; rel=preload; as="style", </_next/static/css/d919a23d3e0f46ca.css>; rel=preload; as="style", </_next/static/css/9654700e0b05c4f3.css>; rel=preload; as="style", </_next/static/css/6a8dad7541c395c5.css>; rel=preload; as="style", </_next/static/css/875521df20fe266b.css>; rel=preload; as="style", </_next/static/css/90008081493b87d1.css>; rel=preload; as="style", </_next/static/css/d0d54c6a4c836f31.css>; rel=preload; as="style", </_next/static/css/3de7683961bb0f06.css>; rel=preload; as="style", </_next/static/css/bf43b2b007143a24.css>; rel=preload; as="style", </_next/static/css/3843ea905d94a307.css>; rel=preload; as="style", </_next/static/css/3e663fb6b6fb433d.css>; rel=preload; as="style", </_next/static/css/d8e2497e31d908a6.css>; rel=preload; as="style", </_next/static/css/1379d85b418171b6.css>; rel=preload; as="style", </_next/static/css/fb9afd4a6c48f056.css>; rel=preload; as="style", </_next/static/css/171b445f389b8914.css>; rel=preload; as="style", </_next/static/css/019527d0d9e819ac.css>; rel=preload; as="style", </_next/static/css/bf684b03d8cee2cc.css>; rel=preload; as="style", </_next/static/css/670d6a1f8411c615.css>; rel=preload; as="style", </_next/static/css/8a8e6d94ca0425ed.css>; rel=preload; as="style", </_next/static/css/508d3b12bea531da.css>; rel=preload; as="style", </_next/static/css/2f2b5779262b5336.css>; rel=preload; as="style", </_next/static/css/dae97bcab0e8d770.css>; rel=preload; as="style", </_next/static/css/9cda9e32719c4864.css>; rel=preload; as="style", </_next/static/css/c3e7bdcb87b974c7.css>; rel=preload; as="style", </_next/static/css/30ed0e4cd604b851.css>; rel=preload; as="style", </_next/static/css/a42929fa97f0a88b.css>; rel=preload; as="style", </_next/static/css/2753647b87266e75.css>; rel=preload; as="style", </_next/static/css/9bc1ffd3896e0178.css>; rel=preload; as="style", </_next/static/css/2e85c571399b9690.css>; rel=preload; as="style", </_next/static/css/ef46db3751d8e999.css>; rel=preload; as="style", </_next/static/css/e3ab83d986fb163a.css>; rel=preload; as="style", </_next/static/css/1d93d7b7bd1e3bc8.css>; rel=preload; as="style", </_next/static/css/76ab74c9fb351c02.css>; rel=preload; as="style", </_next/static/css/9c849a36bba4d06b.css>; rel=preload; as="style", </_next/static/css/2ddc23bbd2871cbc.css>; rel=preload; as="style", </_next/static/css/32076d267b2d2e69.css>; rel=preload; as="style", </_next/static/css/49765896bcf45ae2.css>; rel=preload; as="style", </_next/static/css/d32c130f11f94088.css>; rel=preload; as="style"
X-Powered-By: Next.js
Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate

5085

 

두번째 Nginx 수정없이 잘 배포되고 있는 사이트의 결과를 보자.

curl -I -s -w '%{size_header}\n' https://old.example.com

 

결과는

HTTP/2 200
date: Wed, 26 Mar 2025 08:49:21 GMT
content-type: text/html; charset=utf-8
content-security-policy: default-src 'self';  script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: repo.whatap-browser-agent.io/rum/prod/ static.nid.naver.com developers.kakao.com  appleid.cdn-apple.com t1.daumcdn.net t1.kakaocdn.net stdpay.inicis.com stdux.inicis.com nice.checkplus.co.kr umami.example.com js.tosspayments.com;  connect-src 'self' rum-ap-northeast-2.whatap-browser-agent.io nice.checkplus.co.kr umami.example.com event.tosspayments.com apigw-sandbox.tosspayments.com apigw.tosspayments.com google-analytics.com overbridgenet.com;  style-src 'self' 'unsafe-inline' stdpay.inicis.com;  font-src 'self' data:;  worker-src 'self' blob:;  img-src 'self' blob: data: img-stg.example.com;  media-src 'self' blob: data: commondatastorage.googleapis.com img-stg.example.com;  frame-src 'self' postcode.map.daum.net stdpay.inicis.com nice.checkplus.co.kr payment-gateway-sandbox.tosspayments.com api.tosspayments.com pay-sandbox.tosspayments.com img-stg.example.com;  frame-ancestors 'self' bo.example.com po.example.com cc.example.com bo-mui.example.com po-mui.example.com cc-mui.example.com;  form-action 'self' stdpay.inicis.com nice.checkplus.co.kr mobile.inicis.com inilite.inicis.com  sharer.kakao.com accounts.kakao.com;
timing-allow-origin: *
referrer-policy: strict-origin-when-cross-origin
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
x-dns-prefetch-control: on
strict-transport-security: max-age=31536000; includeSubDomains
permissions-policy: camera=(), microphone=(), geolocation=()
set-cookie: NEXT_LOCALE=ko; Path=/
set-cookie: lang_cd=ko; Path=/; Expires=Thu, 27 Mar 2025 08:49:20 GMT; Secure
set-cookie: site_no=1; Path=/; Expires=Thu, 27 Mar 2025 08:49:20 GMT; Secure
set-cookie: mall_no=10001; Path=/; Expires=Thu, 27 Mar 2025 08:49:20 GMT; Secure
x-middleware-rewrite: /ko
vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Accept-Encoding
link: </_next/static/media/119cf01b445a4dc3-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/12f0acdcae926a24-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/e00e15f44d7b58c0-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2"
x-powered-by: Next.js
cache-control: private, no-cache, no-store, max-age=0, must-revalidate
x-envoy-upstream-service-time: 447
server: istio-envoy

2459

 

두 개의 결과를 비교해볼 때
우선 신규 application은 헤더가 5.0 kb 정도 (5,085 bytes)
기존 application은 헤더가 2.4 kb가 (2,459 bytes) 나오는 것을 볼 수 있다.

위 두 결과를 비교해보면
Middleware 헤더 사이즈와
Cookies/Auth관련 헤더 사이즈는 동일하다.
그러나 신규 app에서는 pre-loading된 CSS chunk가 너무 많이 나온다.

 

위에는 결과론적이 분석이었고 원래는 502에러가 뜨길래

Nginx에 접속하여 로그를 보니

cd /var/log/nginx
cat error.log

 

2025/03/26 01:53:14 [error] 578#578: *76630 upstream sent too big header while reading response header from upstream

이런 문구가 반복해서 나오고 있었다.

이는 우리가 Ubuntu에 설치한 Nginx의 default buffer limit이 4 kb 여서 생긴 문제다.

 

확인: Nginx 서버 안에서

> getconf PAGESIZE
4096

preload되는 CSS 수정은 시간이 걸리니 우선은 Nginx의 사이즈를 늘릴 필요가 있다.

 

해결

Nginx에 들어가 글로벌한 설정인 /etc/nginx/nginx.conf를 수정하는 방법도 있지만
이 하나에서만 발생한 문제라서 개별 .conf 파일을 수정하기로 하였다.

아래처러 proxy_로 시직하는 3줄을 추가로 넣어주었다.

http {
    ...

    server {
        listen 443;
        server_name new.example.com;

        location / {
            proxy_pass http://118.222.111.22:10010;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;

            # 추가
            proxy_buffer_size          128k;
            proxy_buffers              4 256k;
            proxy_busy_buffers_size    256k;
        }
    }
}

 

현재 설치된 Nginx의 default값은

proxy_buffer_size          4k;
proxy_buffers              8 4k;
proxy_busy_buffers_size    4k;

 

각각 설명
proxy_buffer_size : 4k - 헤더 버퍼 크기
proxy_buffers : 8 4k - Response의 body에 총 8개의 4k 버퍼를 할당한다는 의미
proxy_busy_buffers_size : 4k - upstream에서 더 read를 하는 동안 in-flight의 응답 데이터에 넣어놓는 크기

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유