증상
프로덕션 k-skill-proxy(gpu01, Cloudflare Tunnel 경유 k-skill-proxy.nomadamas.org)의 proxy.log 48일치(2026-06-25 ~ 2026-08-12, 517,675 라인)를 분석한 결과, **429 응답이 91,029건으로 전체 요청의 약 17.6%**를 차지합니다.
| 라우트 |
429 건수 |
/v1/korea-weather/forecast |
24,060 |
/v1/real-estate/:assetType/:dealType |
13,756 |
/v1/naver-news/search |
13,365 |
/v1/building-register/title |
9,206 |
/v1/korean-law/detail |
4,682 |
/v1/korean-stock/trade-info |
4,127 |
/v1/seoul-subway/arrival |
3,028 |
일별 추이: 08-02 14,664건, 08-06 16,911건, 08-11 11,383건 등 버스트성 급증 반복.
원인 (코드 + 배포 설정)
레이트리미터는 클라이언트를 request.ip로 구분합니다 (packages/k-skill-proxy/src/server.js:373).
그런데:
trustProxyHops 기본값이 0이라 (server.js:242) Fastify가 trustProxy: false로 기동됩니다 (server.js:2096).
- gpu01 프로덕션
.env에도 KSKILL_PROXY_TRUST_PROXY_HOPS가 설정되어 있지 않습니다 (KSKILL_PROXY_RATE_LIMIT_WINDOW_MS/KSKILL_PROXY_RATE_LIMIT_MAX만 설정됨).
- 프로덕션은 cloudflared가 localhost로 포워딩하는 구조라,
trustProxy: false 상태에서는 모든 외부 요청의 request.ip가 cloudflared 커넥터 주소(127.0.0.1) 하나로 보입니다.
결과적으로 전체 사용자가 단일 레이트리밋 버킷을 공유합니다. 사용자가 누구든(에이전트 루프 하나든) 버스트를 치면 모두가 함께 429를 받는 구조이며, 관측된 429 폭증과 정확히 일치합니다.
요청하는 수정
- 즉시 조치(운영): gpu01
.env에 KSKILL_PROXY_TRUST_PROXY_HOPS=1 설정 후 서비스 재시작. cloudflared 1홉만 신뢰하면 X-Forwarded-For의 실제 클라이언트 IP로 버킷이 나뉩니다.
- 코드/배포 하드닝:
scripts/deploy-k-skill-proxy-gpu01.sh 또는 배포 문서(docs/deploy-k-skill-proxy.md)에 KSKILL_PROXY_TRUST_PROXY_HOPS=1을 기본값으로 박제하거나, 프로덕션 체크리스트에 포함.
- 선택: Cloudflare Tunnel 전용 배포임을 고려해
CF-Connecting-IP 헤더 우선 사용 옵션 검토 (XFF 스푸핑 방지).
- 회귀 방지: trustProxy 활성 상태에서 서로 다른
X-Forwarded-For를 가진 두 클라이언트가 별도 버킷을 쓰는지 확인하는 테스트 추가 (packages/k-skill-proxy/test/server.test.js).
검증 근거
- 로그 집계: gpu01
/data/home/jeffrey/apps/k-skill-proxy/proxy.log (routeUsage 라인 기준, statusCode별/route별 카운트)
- 코드:
server.js 239-242(config), 365-405(buildRateLimiter), 2093-2097(Fastify init)
- 프로덕션 env:
KSKILL_PROXY_TRUST_PROXY_HOPS 부재 확인 (값 자체는 미열람, 키 존재 여부만 grep)
증상
프로덕션 k-skill-proxy(gpu01, Cloudflare Tunnel 경유
k-skill-proxy.nomadamas.org)의proxy.log48일치(2026-06-25 ~ 2026-08-12, 517,675 라인)를 분석한 결과, **429 응답이 91,029건으로 전체 요청의 약 17.6%**를 차지합니다./v1/korea-weather/forecast/v1/real-estate/:assetType/:dealType/v1/naver-news/search/v1/building-register/title/v1/korean-law/detail/v1/korean-stock/trade-info/v1/seoul-subway/arrival일별 추이: 08-02 14,664건, 08-06 16,911건, 08-11 11,383건 등 버스트성 급증 반복.
원인 (코드 + 배포 설정)
레이트리미터는 클라이언트를
request.ip로 구분합니다 (packages/k-skill-proxy/src/server.js:373).그런데:
trustProxyHops기본값이 0이라 (server.js:242) Fastify가trustProxy: false로 기동됩니다 (server.js:2096)..env에도KSKILL_PROXY_TRUST_PROXY_HOPS가 설정되어 있지 않습니다 (KSKILL_PROXY_RATE_LIMIT_WINDOW_MS/KSKILL_PROXY_RATE_LIMIT_MAX만 설정됨).trustProxy: false상태에서는 모든 외부 요청의request.ip가 cloudflared 커넥터 주소(127.0.0.1) 하나로 보입니다.결과적으로 전체 사용자가 단일 레이트리밋 버킷을 공유합니다. 사용자가 누구든(에이전트 루프 하나든) 버스트를 치면 모두가 함께 429를 받는 구조이며, 관측된 429 폭증과 정확히 일치합니다.
요청하는 수정
.env에KSKILL_PROXY_TRUST_PROXY_HOPS=1설정 후 서비스 재시작. cloudflared 1홉만 신뢰하면X-Forwarded-For의 실제 클라이언트 IP로 버킷이 나뉩니다.scripts/deploy-k-skill-proxy-gpu01.sh또는 배포 문서(docs/deploy-k-skill-proxy.md)에KSKILL_PROXY_TRUST_PROXY_HOPS=1을 기본값으로 박제하거나, 프로덕션 체크리스트에 포함.CF-Connecting-IP헤더 우선 사용 옵션 검토 (XFF 스푸핑 방지).X-Forwarded-For를 가진 두 클라이언트가 별도 버킷을 쓰는지 확인하는 테스트 추가 (packages/k-skill-proxy/test/server.test.js).검증 근거
/data/home/jeffrey/apps/k-skill-proxy/proxy.log(routeUsage 라인 기준, statusCode별/route별 카운트)server.js239-242(config), 365-405(buildRateLimiter), 2093-2097(Fastify init)KSKILL_PROXY_TRUST_PROXY_HOPS부재 확인 (값 자체는 미열람, 키 존재 여부만 grep)