문제
개발기 서버로 사용하는 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의 응답 데이터에 넣어놓는 크기
'DevOps와 Infra > DevOps 일반' 카테고리의 다른 글
Docker Monitoring & Management Tools 추천 (0) | 2025.03.28 |
---|---|
Grafana에서 Alert 생성 (2) - 설치 및 설정 (0) | 2025.02.28 |
Grafana에서 Alert 생성 (1) - MS Teams (0) | 2025.02.26 |
GitLab 에서 merge request 하는 방법 (0) | 2025.02.21 |
Atlassian Bamboo란? (1) | 2025.02.18 |