2026年11月12日
2026年11月12日
Nginx GeoIP2로 국가별 접속 제한하기|GeoLite2 업데이트와 WordPress 관리자 화면 제한
이 글은 일본어 원문을 현지화한 버전입니다. 일본어 원문 보기
Nginx에서 국가별로 접속을 제한하려면, GeoIP2용 모듈(ngx_http_geoip2_module)과 MaxMind의 GeoLite2 데이터베이스를 사용해 geoip2 블록에서 국가 코드를 변수에 담고, map으로 허용·거부를 결정하는 것이 2026년 11월 현재의 방법입니다. 오래된 글에 나오는 "GeoLite의 .dat 파일을 wget으로 받는" 절차는 데이터 배포가 종료되었기 때문에 동작하지 않습니다.
결론부터 정리합니다.
- 구
ngx_http_geoip_module(GeoIP Legacy)은 MaxMind가 구 형식 데이터 배포를 종료했기 때문에 새로 사용할 수 없습니다. GeoIP2(.mmdb형식)로 옮겨야 합니다. - GeoLite2는 무료이지만 MaxMind 계정 등록과 라이선스 키가 필요하며, 다운로드와 갱신은
geoipupdate로 합니다. - Nginx에서는
geoip2블록으로$geoip2_country_code같은 변수를 만들고,map으로allow/deny에 해당하는 0 / 1을 정한 뒤,location에서return 403을 반환합니다. - Cloudflare 같은 CDN을 거치는 사이트에서는 먼저
real_ip_header로 방문자의 실제 IP를 복원하지 않으면 모든 접속이 CDN의 국가로 판정됩니다. - 국가별 제한은 "공격을 줄이는 보조 수단"이며 VPN이나 중계로 우회할 수 있습니다. 공개 페이지 전체가 아니라 로그인 화면과 관리자 화면에 한정해 쓰는 것이 현실적입니다.
구 geoip와 GeoIP2의 차이
| 항목 | ngx_http_geoip_module(구) | ngx_http_geoip2_module(GeoIP2) |
|---|---|---|
| 데이터 형식 | GeoIP Legacy(.dat) |
GeoIP2(.mmdb) |
| 무료 데이터 제공 | 종료(2019년에 배포 종료) | GeoLite2(계정 등록과 라이선스 키 필요) |
| 필요한 라이브러리 | libGeoIP | libmaxminddb |
| 모듈 입수 | Nginx 기본 제공(--with-http_geoip_module) |
서드파티 모듈 leev/ngx_http_geoip2_module. Debian / Ubuntu는 패키지 제공 |
| 변수 | $geoip_country_code 등 고정 |
geoip2 블록에서 직접 이름을 지정 |
기존 설정에서 geoip_country를 사용 중이라면 데이터베이스 갱신이 멈춰 있어 판정이 낡은 상태입니다. 이 기회에 GeoIP2로 옮기세요.
1단계: MaxMind에 등록하고 라이선스 키 만들기
- MaxMind의 GeoLite2 페이지에서 무료 계정을 만듭니다.
- 로그인한 뒤 계정 관리 화면에서 License Key를 발급합니다(키는 발급할 때만 표시되므로 반드시 적어 둡니다).
- 사용할 데이터베이스는 국가만 필요하면
GeoLite2-Country, 도시까지 필요하면GeoLite2-City입니다. 국가별 제한이라면 Country로 충분합니다.
이용 조건(GeoLite2 End User License Agreement)에는 데이터 재배포와 표시에 관한 규정이 있습니다. 사내 서버에서 판정에만 사용하는 경우라면 보통 허용 범위이지만, 등록할 때 조건을 확인하세요.
2단계: geoipupdate로 데이터베이스 받기·갱신하기
GeoLite2는 정기적으로 갱신되므로 직접 내려받지 말고 geoipupdate를 사용합니다(geoipupdate 문서).
# Debian / Ubuntu
sudo apt install geoipupdate
# RHEL 계열(AlmaLinux / Rocky Linux)은 EPEL에서
sudo dnf install geoipupdate
설정 파일 /etc/GeoIP.conf에 계정 ID와 라이선스 키를 적습니다.
AccountID 123456
LicenseKey 여기에 발급받은 키
EditionIDs GeoLite2-Country
# 저장 위치는 배포판 기본값입니다(Debian / Ubuntu는 /var/lib/GeoIP, 그 밖에는 /usr/share/GeoIP인 경우가 많음).
# 바꿀 때는 DatabaseDirectory /var/lib/GeoIP
sudo geoipupdate -v
ls -l /var/lib/GeoIP/ # GeoLite2-Country.mmdb 가 있으면 성공
패키지 버전의 geoipupdate에는 systemd 타이머나 cron으로 정기 실행하는 설정이 들어 있습니다(systemctl list-timers | grep geoip로 확인). 없다면 주 1회 정도 실행하는 cron을 추가합니다.
3단계: Nginx에 GeoIP2 모듈 설치하기
Debian / Ubuntu(apt로 설치한 Nginx)
sudo apt install libnginx-mod-http-geoip2
ls /etc/nginx/modules-enabled/ # 50-mod-http-geoip2.conf 가 생성됨
nginx -t
nginx.org 패키지나 소스 빌드로 설치한 Nginx
libmaxminddb를 설치한 다음, 실행 중인 Nginx와 같은 버전의 소스에서 동적 모듈을 빌드합니다.
sudo apt install libmaxminddb-dev # RHEL 계열은 libmaxminddb-devel
cd /usr/local/src
git clone https://github.com/leev/ngx_http_geoip2_module
curl -LO https://nginx.org/download/nginx-1.26.3.tar.gz # 버전은 nginx -v에 맞춤
tar xzf nginx-1.26.3.tar.gz && cd nginx-1.26.3
./configure --with-compat --add-dynamic-module=../ngx_http_geoip2_module
make modules
sudo cp objs/ngx_http_geoip2_module.so /etc/nginx/modules/
nginx.conf 맨 위에서 로드합니다(패키지 버전에서는 자동으로 배치됩니다).
load_module modules/ngx_http_geoip2_module.so;
4단계: 국가 코드를 변수로 만들어 허용·거부 결정하기
http 블록에 작성합니다. geoip2 블록에서 국가 코드를 변수에 담고, map으로 허용할 국가는 1, 그 밖은 0으로 지정합니다.
http {
geoip2 /var/lib/GeoIP/GeoLite2-Country.mmdb {
auto_reload 60m;
$geoip2_country_code country iso_code;
}
# 관리자 화면을 허용할 국가(ISO 3166-1의 2글자 코드)
map $geoip2_country_code $admin_allowed {
default 0;
JP 1;
"" 1; # 판정할 수 없는 경우(사설 IP 등)는 허용. 거부하려면 0
}
}
auto_reload는 geoipupdate가 교체한 데이터베이스를 재시작 없이 다시 읽어 들이는 간격입니다.
5단계: WordPress 로그인 화면과 관리자 화면만 제한하기
공개 페이지는 제한하지 않고 wp-login.php와 /wp-admin/만 국가로 좁히는 예입니다. server 블록에 작성합니다.
server {
# ... 기존 설정 ...
location = /wp-login.php {
if ($admin_allowed = 0) { return 403; }
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ^~ /wp-admin/ {
if ($admin_allowed = 0) { return 403; }
# admin-ajax.php를 외부 플러그인이 사용하는 경우에는 별도 location에서 허용
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
}
if와 return의 조합은 Nginx가 안전하게 처리할 수 있는 형태입니다. /wp-admin/admin-ajax.php는 공개 페이지의 기능(폼이나 무한 스크롤 등)이 로그인하지 않은 상태에서 호출하는 경우가 있으므로, 제한에서 제외할지는 사이트의 기능에 맞춰 결정합니다.
적용과 확인:
sudo nginx -t && sudo systemctl reload nginx
# 자국에서 접속하면 200 또는 302가 반환됨
curl -sI https://example.com/wp-login.php | head -1
# 판정 확인(로그에 국가 코드를 출력하는 예)
# log_format geo '$remote_addr $geoip2_country_code "$request" $status';
해외에서 어떻게 보이는지 확인하려면 해외 거점의 VPN이나 국외 모니터링 서비스에서 wp-login.php에 접속해 403이 반환되는지 확인합니다.
CDN(Cloudflare 등)을 거치는 경우
CDN을 거치면 $remote_addr가 CDN의 IP가 되어 모든 방문자가 CDN의 국가로 판정됩니다. 먼저 ngx_http_realip_module로 방문자의 실제 IP를 복원합니다(realip 모듈).
# Cloudflare의 예. IP 목록은 Cloudflare 공식 IP Ranges에서 정기적으로 갱신
set_real_ip_from 173.245.48.0/20;
# ... Cloudflare의 전체 범위를 나열 ...
real_ip_header CF-Connecting-IP;
geoip2는 기본적으로 $remote_addr를 참조하므로, realip로 교체된 뒤의 실제 IP로 판정됩니다. Cloudflare 자체에도 국가별 접근 제어(WAF 사용자 지정 규칙)가 있으므로, Cloudflare를 사용 중이라면 Nginx가 아니라 Cloudflare 쪽에서 제한하는 편이 설정이 간단합니다. WordPress에 Cloudflare 설정하기도 참고하세요.
국가별 제한의 한계
- GeoIP 판정은 IP 주소의 등록 정보에 근거한 추정이며, VPN·프록시·해외 클라우드를 통한 중계로 우회할 수 있습니다.
- 모바일 회선이나 기업 네트워크에서는 실제 위치와 다른 국가로 판정되는 경우가 있습니다. 해외 출장 중인 관리자가 접속하지 못하게 되는 운영상의 문제도 생깁니다.
- 따라서 국가별 제한은 로그인 시도 무차별 대입을 줄이는 보조 수단으로, 2단계 인증이나 로그인 횟수 제한, Basic 인증으로 wp-admin 보호하기와 함께 사용합니다.
자주 묻는 질문
Nginx의 geoip 모듈과 geoip2 모듈은 어떻게 다른가요?
구 geoip 모듈은 GeoIP Legacy 형식(.dat) 데이터를 읽는데, MaxMind는 이 형식의 배포를 종료했습니다. geoip2 모듈(ngx_http_geoip2_module)은 현재의 .mmdb 형식을 읽으며 GeoLite2 무료 데이터를 사용할 수 있습니다. 새로 설정한다면 geoip2를 사용하세요.
GeoLite2는 무료인가요? 등록이 필요한가요?
무료이지만 MaxMind 계정 등록과 라이선스 키 발급이 필요합니다. 다운로드와 정기 갱신은 geoipupdate로 합니다. 키 없이 직접 내려받던 예전 방법은 동작하지 않습니다.
국가별로 제한하면 WordPress 무단 로그인을 막을 수 있나요?
무차별 대입 시도를 줄이는 효과는 있지만, VPN이나 해외 서버를 통한 중계로 우회할 수 있으므로 막을 수 있다고는 말할 수 없습니다. 로그인 횟수 제한, 2단계 인증, Basic 인증 등과 함께 사용하세요.
Cloudflare를 쓰면 모든 접속이 같은 국가로 나옵니다
CDN을 거치면 접속 출발지가 CDN의 IP가 되기 때문입니다. set_real_ip_from과 real_ip_header CF-Connecting-IP로 실제 IP를 복원한 뒤에 판정하세요. Cloudflare의 WAF로 국가별로 제한하는 방법도 있습니다.
GeoLite2-City를 쓰면 도시 단위까지 제한할 수 있나요?
도시나 지역 코드도 변수에 담을 수 있지만, 도시 수준의 정확도는 국가보다 낮아 오판정으로 정상적인 이용자를 차단하기 쉽습니다. 접근 제어에는 국가 수준까지만 사용하는 것이 무난합니다.
정리
- 현재 Nginx의 국가별 제한은 GeoIP2(ngx_http_geoip2_module + GeoLite2
.mmdb)로 하고, 데이터는geoipupdate로 갱신합니다. geoip2로 국가 코드를 변수에 담고,map으로 허용·거부를 정하고,wp-login.php와/wp-admin/의location에서return 403을 반환합니다.- CDN을 거친다면 먼저 실제 IP를 복원하고, 국가별 제한은 2단계 인증 등과 함께 쓰는 보조책으로 사용합니다.
관련 글
- WordPress REST API 접근을 제한하는 방법
- htpasswd로 Apache Basic 인증 비밀번호 만들기|wp-admin 보호
- WordPress에 Cloudflare 설정하기 완전 가이드
참고: ngx_http_geoip2_module(GitHub), MaxMind GeoLite2 Free Geolocation Data, MaxMind – Updating Databases(geoipupdate), Nginx ngx_http_realip_module
업데이트 이력: 2026-11-09 전면 개정(배포가 종료된 구 GeoLite 절차를 GeoIP2·geoipupdate 절차로 교체하고, WordPress 관리자 화면 제한 예시와 CDN 주의 사항을 추가). 이 글은 WordPress Hakase(라이트센드 주식회사)가 운영합니다.
WordPress에 대해 궁금한 점이 있으신가요? 이 사이트의 운영사는 일본에 있으며 일본어로 답변합니다. 문의하기(일본어)