Nginx rate limit으로 과도한 요청 막기
접속 로그를 분석해 보면 같은 IP가 1초에 수십 번씩 로그인 페이지나 검색 주소를 두드리는 경우를 쉽게 볼 수 있다. Nginx의 limit_req 기능을 쓰면 IP별로 요청 속도를 제한해 이런 과도한 요청을 서버 앞단에서 막을 수 있다. 이 글은 Nginx rate limit 설정 방법과 값 정하는 법, 확인 방법을 정리한다. 로그에서 문제 IP를 찾는 방법은 GoAccess 로그 분석에 먼저 정리해 두었다.

Nginx rate limit 동작 방식
limit_req는 물이 일정한 속도로 빠지는 양동이(leaky bucket)처럼 동작한다. 정해진 속도(rate)보다 빨리 들어오는 요청은 버스트(burst) 크기만큼 대기열에 쌓이고, 그것마저 넘치면 거절된다.
| 설정 | 뜻 |
|---|---|
zone | IP별 요청 기록을 저장할 공유 메모리(10m이면 약 16만 개 IP) |
rate | 허용 속도(10r/s = 초당 10개, 5r/m = 분당 5개) |
burst | 잠깐 몰리는 요청을 허용할 여유분 |
nodelay | 여유분 안의 요청은 기다리지 않고 바로 처리 |
Nginx rate limit 기본 설정
# /etc/nginx/nginx.conf 의 http 블록 안
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_status 429;
# server 블록 안
location / {
limit_req zone=perip burst=20 nodelay;
...
}
일반 페이지는 초당 10개에 순간 20개까지 여유를 두고, 넘치면 429(Too Many Requests)를 돌려준다. 이미지와 CSS가 많은 페이지는 한 번에 요청이 몰리니 burst를 너무 작게 잡지 않는다.
Nginx rate limit 로그인 페이지 보호
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock; # 서버의 PHP 버전에 맞게
}
워드프레스 로그인은 분당 5회 정도면 사람이 쓰기에 충분하고, 비밀번호 대입 공격은 크게 줄어든다. location을 따로 만들면 PHP 처리 설정도 그 안에 넣어야 한다는 점을 잊지 말자. 워드프레스 보안 전반은 워드프레스 보안 설정에 있다.
적용과 확인
sudo nginx -t && sudo systemctl reload nginx
# 빠르게 30번 요청해 429 가 섞이는지 확인
for i in $(seq 30); do curl -s -o /dev/null -w "%{http_code}\n" https://example.com/; done | sort | uniq -c
# 제한에 걸린 기록
sudo grep "limiting requests" /var/log/nginx/error.log | tail
예외 처리
내 사무실 IP나 모니터링 서버처럼 제한하면 안 되는 주소는 geo와 map으로 키를 빈 값으로 만들어 제외할 수 있다. 빈 키는 제한 대상에서 빠진다.
geo $limit {
default 1;
203.0.113.10 0; # 예외 IP
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=perip:10m rate=10r/s;
설정 항목은 Nginx limit_req 모듈 문서에 자세히 나와 있다. 반복 공격 IP를 아예 차단하려면 fail2ban과 함께 쓰면 좋다.
마무리
rate limit은 정상 사용자에게는 거의 느껴지지 않으면서 자동화된 과다 요청을 크게 줄여 준다. 처음에는 넉넉한 값으로 시작해 error.log를 보며 조정하자. 전날 글은 서버 자원 알림 스크립트다.
첫 댓글을 남겨보세요