로그인 API에 무차별 대입 막으려고 express-rate-limit 붙였어요. IP당 15분에 100회로 넉넉하게 잡았는데, 트래픽 몰리는 시간대가 되면 멀쩡한 사용자들까지 429 Too Many Requests를 뱉습니다.
구성은 사용자 → Cloudflare → Nginx 리버스 프록시 → Node(Express) 이렇게 돼 있어요. 로그를 보니 rate limit이 세는 IP가 죄다 우리 Nginx 내부 IP 하나로 찍혀 있더라고요.
그러니까 전 세계 사용자가 전부 같은 카운터를 공유해서 순식간에 100회를 넘겨버리는 상황인 것 같습니다. 실제 클라이언트 IP로 세게 하려면 어떻게 설정하나요?
댓글 2
핵심은 실제 클라이언트 IP를 X-Forwarded-For 헤더에서 읽게 하는 건데, Express한테 프록시를 신뢰하라고 알려줘야 합니다. app.set('trust proxy', ...)를 설정하면 req.ip가 remoteAddress 대신 X-Forwarded-For를 파싱한 실제 클라이언트 IP를 돌려주고, express-rate-limit은 기본적으로 req.ip로 키를 잡으니 그때부터 사용자별로 제대로 세집니다.
다만 여기서 흔히 하는 실수가 app.set('trust proxy', true)로 전부 믿어버리는 거예요. 그러면 아무나 X-Forwarded-For 헤더를 위조해서 rate limit을 우회하거나 남의 IP로 위장할 수 있습니다. 신뢰할 프록시 홉 수만큼만 믿게 숫자로 주세요. 예를 들어 Nginx가 바로 앞이면 app.set('trust proxy', 1) 처럼 홉 수를 지정하거나, 신뢰하는 프록시 IP 대역을 직접 지정하는 방식이 안전합니다.
확인은 간단해요. 설정 후 아무 라우트에서 req.ip를 찍어보세요. 진짜 클라이언트 IP가 나오면 성공입니다. Nginx 쪽도 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 가 들어가 있는지 같이 확인하시고요. 그리고 Cloudflare가 맨 앞이면 원본 IP는 CF-Connecting-IP 헤더에 있으니, 정밀하게 하려면 keyGenerator에서 그 헤더를 우선으로 읽게 커스텀하는 것도 방법이에요.
하나 더: 최신 express-rate-limit은 trust proxy가 true(전부 신뢰)면 위험하다고 startup에서 경고나 에러를 냅니다. 그 메시지가 뜨면 지금 이 문제라는 신호예요.