개발

Nginx IP 기반 요청 제한 거는 방법 (limit_req 모듈)

by 에루샤
Nginx IP 기반 요청 제한 거는 방법 (limit_req 모듈)
목차
우리가 흔히 웹서버를 운용하다보면 동시접속을 과도하게 받는 경우가 있다.
이런 동시접속 처리 자체는 서버가 잘 운용되고있고 대중화되었다는 지표이긴하나 통상적이지 않은 요청까지도 커버를 쳐야하는 상황에서는 그렇게 유쾌하진 않은 경우도 있다.

2025-06-08T23:56:10+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:10+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:10+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:10+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:10+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:10+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:10+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:10+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:11+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:11+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:11+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:11+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
2025-06-08T23:56:11+00:00 | 200 | jbgogo.or.kr | /bbs/notice/1305 | 79629 | 59.7.91.102 | https://jbgogo.or.kr/bbs/notice | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
Copy

예를들어 위와 같은 서버 로그 기록이 있다치면 이걸 통상적인 요청으로 받아들여야하는지, 악의적인 요청으로 받아들여야하는지에 대한 딜레마가 발생한다고 보면 된다.

사용자가 특정 페이지를 여러번 요청하는거야 가능한 하지만 물리적으로 한 페이지를 1초내에 3번 5번 요청하는건 불가능하다.
즉 위 로그처럼 요청한다는거는 악의적인 요청으로 페이지를 접근한다던지, 아니면 기계적인 방식으로 페이지를 요청하는 경우일 것이다.

이런 경우에 서버는 이런 요청을 오롯이 감수해야할 이유가 전혀 없으므로 이렇게 특정 시간내에 과도하게 요청이 오는경우를 대비해 요청 제한을 거는방법이 존재한다.


요청 제한 (limit_req)

빠르게 적용하는방법부터 알아보자.

우선 http 블럭에 아래와 같이 limit_req_zone을 선언해준다.

http {
    limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=3r/s;
    limit_req_status 429;

    ...Copy

이 선언문의 구조는 아래와 같다.
서버에 요청되어오는 IP($binary_remote_addr)를 기준으로 1초에 3번까지의 Reqeust(rate=3r/s)를 허용하는 영역을 선언해둔다.
이 영역에 기록되는 IP값은 최대 10M 파일에 저장(zone=req_per_ip:10m)되며 여기에는 대략 16만개의 아이피가 저장될 수 있다.

그리고 이렇게 요청 제한으로 인한 서버 차단에러는 기본으로 503 Service Temporarily Unavailable 에러가 뜨기때문에 이를 429 Too Many Requests 상태로 바꿔주는 구문을 입력하면 요청제한 설정 작업이 완료된다.

그리고 이를 적용할 server 블록내 location 에 아래와 같이 적용해주면 된다.

server {
    listen 80;
    server_name _;
        
    location / {
        limit_req zone=req_per_ip burst=5 nodelay;

        ...
Copy

이 limit_req는 꼭 location 블록내에 지정되어야하며 위에서 선언한 영역을 지정해줘야 작동된다.
burst=5는 초당 3건이후에 실제로 요청차단이 이루어지는 임계값을 뜻한다.

즉 위 설정구문대로 작동한다면 총 10개의 요청이 들어온다면,

1-3: 정상 허용(rate=3r/s)
4-8: 임계 허용(burst=5)
9-10: 차단

이렇게 9번째 요청부터 차단된다고 보면된다.
그리고 더이상 요청이 없는 경우 3r/s 값에 의거해 요청을 처리할 수 있는 토큰이 쌓이게되고 이 값이 burst 허용치에 누적이되어 회복이 되는 느낌이라고 보면된다.
적립식 토큰의 상한치가 burst 치라 보면된다. (비슷한 개념으로 AWS EC2의 CPU 크레딧이 있다.)


그리고 nodelay 라는 옵션을 알고 가야한다.
이 nodelay는 오버되는 요청을 어떻게 처리할것인가에 대한내용인데, nodelay 옵션을 입력하면 초과되는 요청에 대해 즉시 처리를 하나 burst 수치가 차면 그 이후의 요청은 전부 커트 시키는 개념이다.
반대로 이 옵션을 빼면 추가요청에 대해서 지연 큐에 넣어두고 서버가 초당 처리할수있는 요청만큼 빼서 요청 처리를 지원한다.

결국 지연 큐의 사용여부를 결정하는 개념이라보면된다.


설정시 주의해야할 점

그렇다고 이런 허용/임계수치를 너무 빡빡하게 걸어놓으면 예상치 못한 문제가 발생하기도 한다.
왜냐하면 location / { } 블록에 이 limit_req 기능을 걸어놓으면 웹서버가 받는 '모든'요청에 대해 이런 처리를 한다는점이다.

일례로 어느 index.html 페이지에서 style.css, script.js 등 부속 파일을 사용한다했을때 index.html 로의 서버가 받는요청은 1회가 아니라 스타일과 스크립트 파일까지합해서 3회로 인식을 한다.
그렇기 때문에 이 초당요청수와 임계값을 너무 낮게 설정해두면 컨텐츠가 많은 본 페이지의 리소스도 다 못긁어오는 참사가 벌어질수도 있다.


하지만 서버블록에서 아래와 같이 파일 구조에 따라서 location을 분리해서 사용하면 이를 회피할수는 있긴한데,
# 정적 파일 경로 (limit_req 제외)
location ~* \.(css|js|gif|jpe?g|png|svg|webp|ico|woff2?|ttf|eot|mp4|webm|ogg|webmanifest|avif|bmp|tiff|woff|otf|wasm|json|map|txt)$ {
    root /var/www/html;
}

# 나머지 요청만 제한
location / {
    limit_req zone=req_per_ip burst=5 nodelay;
    root /var/www/html;
}Copy

이 방식은 결국 악의적인 접근에 대해서는 예외가 되는 파일 확장명이 존재한다라는 문제가 있고, 서버블록의 관리가 더욱 복잡해진다는 단점이 있다.

결국 어느정도선에서 자신의 웹서비스에 맞는 제한 값 조정이 필요한건 필수라고 생각한다.

소/중형 사이트는 위 설정값처럼 초당 3회, 버스트 5회정도만해서 최대 8회까지의 요청정도만 막아도 그러려니 운용이되지만 대형사이트라면 가져다쓰는 리소스도 많고 ajax같은 비동기 기능을 통해서도 여러가지가 요청되므로 초당 10회 이상의 설정값이 필요할것이다.

위와는 다르지만 네이버나 구글도 API요청은 초당 10회로 막고있는데, 같은맥락의 개념이 아닐까 싶다.
나는 현재 초당15회요청 처리에 버스트값은 30, nodelay 옵션을 쓰고있다.

limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=10r/s;
limit_req_status 429;

limit_req zone=req_per_ip burst=30;Copy

끝!
#Nginx
0 개의 댓글
×