2026年11月12日

2026年11月12日

Restringir el acceso por país en Nginx con GeoIP2 y GeoLite2

Este artículo es una edición localizada del original en japonés. Leer el original en japonés

Para restringir el acceso por país en Nginx, usa el módulo para GeoIP2 (ngx_http_geoip2_module) con la base de datos GeoLite2 de MaxMind, guarda el código de país en una variable con un bloque geoip2 y decide con map si se permite o se deniega. Ese es el procedimiento vigente a noviembre de 2026. El paso de muchos artículos antiguos, "descargar el .dat de GeoLite con wget", ya no funciona porque esa distribución se terminó.

Primero, el resumen.

  • El antiguo ngx_http_geoip_module (GeoIP Legacy) no sirve para instalaciones nuevas, porque MaxMind dejó de distribuir el formato antiguo. Hay que pasar a GeoIP2 (formato .mmdb).
  • GeoLite2 es gratuito, pero exige una cuenta de MaxMind y una clave de licencia; la descarga y la actualización se hacen con geoipupdate.
  • En Nginx, crea con un bloque geoip2 una variable como $geoip2_country_code, usa map para obtener un 0 / 1 equivalente a allow / deny y devuelve return 403 en el location.
  • Si el sitio pasa por una CDN como Cloudflare, primero hay que recuperar la IP real del visitante con real_ip_header; si no, todas las visitas se detectan como del país de la CDN.
  • La restricción por país es solo una ayuda para reducir ataques y se puede eludir con una VPN o un intermediario. Lo realista es aplicarla a la pantalla de inicio de sesión y al panel de administración, no a todas las páginas públicas.
Flujo de configuración de GeoIP2 en Nginx: registro en MaxMind, obtención de la base de datos con geoipupdate, carga del módulo, decisión con geoip2 y map, y restricción en location

Diferencias entre el antiguo geoip y GeoIP2

Concepto ngx_http_geoip_module (antiguo) ngx_http_geoip2_module (GeoIP2)
Formato de datos GeoIP Legacy (.dat) GeoIP2 (.mmdb)
Datos gratuitos Terminados (distribución cerrada en 2019) GeoLite2 (requiere cuenta y clave de licencia)
Biblioteca necesaria libGeoIP libmaxminddb
Cómo se obtiene el módulo Incluido en Nginx (--with-http_geoip_module) Módulo de terceros leev/ngx_http_geoip2_module. Debian / Ubuntu lo ofrecen como paquete
Variables Fijas, como $geoip_country_code Les pones nombre tú mismo en el bloque geoip2

Si en tu configuración actual usas geoip_country, la base de datos ya no se actualiza y las detecciones están desfasadas. Aprovecha para pasar a GeoIP2.

Paso 1: registrarte en MaxMind y crear la clave de licencia

  1. Crea una cuenta gratuita desde la página de GeoLite2 de MaxMind.
  2. Tras iniciar sesión, genera una License Key en el panel de la cuenta (la clave solo se muestra al generarla, así que anótala).
  3. La base de datos que se usa es GeoLite2-Country si solo necesitas el país, o GeoLite2-City si necesitas la ciudad. Para restringir por país basta Country.

Las condiciones de uso (GeoLite2 End User License Agreement) incluyen reglas sobre la redistribución y la presentación de los datos. Usarlos solo para decidir en tus propios servidores entra normalmente en lo permitido, pero revisa las condiciones al registrarte.

Paso 2: obtener y actualizar la base de datos con geoipupdate

GeoLite2 se actualiza con regularidad, así que no la descargues a mano: usa geoipupdate (documentación de geoipupdate).

# Debian / Ubuntu
sudo apt install geoipupdate

# En la familia RHEL (AlmaLinux / Rocky Linux), desde EPEL
sudo dnf install geoipupdate

Escribe el ID de cuenta y la clave de licencia en el archivo de configuración /etc/GeoIP.conf.

AccountID 123456
LicenseKey aquí la clave que generaste
EditionIDs GeoLite2-Country
# El destino es el predeterminado de cada distribución (Debian / Ubuntu usan /var/lib/GeoIP; en el resto suele ser /usr/share/GeoIP).
# Para cambiarlo: DatabaseDirectory /var/lib/GeoIP
sudo geoipupdate -v
ls -l /var/lib/GeoIP/   # si aparece GeoLite2-Country.mmdb, todo ha ido bien

El paquete de geoipupdate ya trae la ejecución periódica configurada con un temporizador de systemd o con cron (compruébalo con systemctl list-timers | grep geoip). Si no, añade una tarea de cron semanal.

Paso 3: instalar el módulo GeoIP2 en Nginx

Debian / Ubuntu (Nginx de apt)

sudo apt install libnginx-mod-http-geoip2
ls /etc/nginx/modules-enabled/   # se coloca 50-mod-http-geoip2.conf
nginx -t

Nginx de los paquetes de nginx.org o compilado desde el código fuente

Instala libmaxminddb y compila el módulo dinámico a partir del código fuente de la misma versión de Nginx que tienes en ejecución.

sudo apt install libmaxminddb-dev   # en la familia 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   # la versión debe coincidir con 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/

Cárgalo al principio de nginx.conf (con los paquetes de la distribución se coloca automáticamente).

load_module modules/ngx_http_geoip2_module.so;

Paso 4: guardar el código de país en una variable y decidir permitir o denegar

Se escribe en el bloque http. El bloque geoip2 guarda el código de país en una variable y map asigna 1 a los países permitidos y 0 al resto.

http {
    geoip2 /var/lib/GeoIP/GeoLite2-Country.mmdb {
        auto_reload 60m;
        $geoip2_country_code country iso_code;
    }

    # Países que pueden acceder al panel de administración (código de 2 letras de ISO 3166-1)
    map $geoip2_country_code $admin_allowed {
        default 0;
        JP      1;
        ""      1;   # Si no se puede determinar (IP privadas, etc.), se permite. Pon 0 si prefieres denegar
    }
}

auto_reload es el intervalo con el que se vuelve a leer, sin reiniciar, la base de datos que geoipupdate ha reemplazado.

Paso 5: restringir solo el inicio de sesión y el panel de administración de WordPress

Este ejemplo no restringe las páginas públicas y limita por país solo wp-login.php y /wp-admin/. Se escribe en el bloque server.

server {
    # ... configuración existente ...

    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; }
        # Si un plugin externo usa admin-ajax.php, permítelo en otro location
        location ~ \.php$ {
            include        fastcgi_params;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_pass   unix:/run/php/php8.3-fpm.sock;
        }
    }
}

La combinación de if y return es una forma que Nginx maneja de manera segura. /wp-admin/admin-ajax.php puede ser llamado sin sesión iniciada por funciones de las páginas públicas (formularios, scroll infinito, etc.), así que decide si lo excluyes de la restricción según las funciones de tu sitio.

Aplicar y comprobar:

sudo nginx -t && sudo systemctl reload nginx

# Desde tu propio país: devuelve 200 o 302
curl -sI https://example.com/wp-login.php | head -1
# Comprobar la detección (ejemplo para mostrar el código de país en el registro)
# log_format geo '$remote_addr $geoip2_country_code "$request" $status';

Para ver cómo se ve desde el extranjero, accede a wp-login.php con una VPN con salida en otro país o desde un servicio de monitorización externo y comprueba que devuelve 403.

Si pasas por una CDN (Cloudflare, etc.)

Con una CDN, $remote_addr es la IP de la CDN y todos se detectan como del país de la CDN. Primero recupera la IP real del visitante con ngx_http_realip_module (módulo realip).

# Ejemplo para Cloudflare. Actualiza periódicamente la lista de IP desde IP Ranges, la página oficial de Cloudflare
set_real_ip_from 173.245.48.0/20;
# ... enumera todos los rangos de Cloudflare ...
real_ip_header   CF-Connecting-IP;

geoip2 mira por defecto $remote_addr, así que la detección se hace con la IP real ya sustituida por realip. Cloudflare también tiene su propio control de acceso por país (reglas personalizadas de WAF), de modo que si usas Cloudflare es más sencillo restringir allí que en Nginx. Consulta también Cómo configurar Cloudflare en WordPress.

Límites de la restricción por país

  • La detección de GeoIP es una estimación basada en los datos de registro de las direcciones IP y se puede eludir con VPN, proxies o intermediarios en la nube de otro país.
  • En redes móviles o corporativas, a veces se detecta un país distinto del real. También hay un problema operativo: un administrador de viaje en el extranjero puede quedarse fuera.
  • Por eso, la restricción por país se usa como ayuda para reducir los ataques de fuerza bruta al inicio de sesión, combinada con la autenticación en dos pasos, el límite de intentos de acceso y la protección de wp-admin con autenticación Basic.

Preguntas frecuentes

¿Qué diferencia hay entre el módulo geoip y el módulo geoip2 de Nginx?

El antiguo módulo geoip lee datos en formato GeoIP Legacy (.dat), pero MaxMind terminó la distribución de ese formato. El módulo geoip2 (ngx_http_geoip2_module) lee el formato actual .mmdb y permite usar los datos gratuitos de GeoLite2. Las configuraciones nuevas deben hacerse con geoip2.

¿GeoLite2 es gratuito? ¿Hay que registrarse?

Es gratuito, pero requiere una cuenta de MaxMind y generar una clave de licencia. La descarga y la actualización periódica se hacen con geoipupdate. El procedimiento antiguo de descargar directamente sin clave ya no funciona.

¿Restringir por país evita los accesos no autorizados a WordPress?

Reduce los intentos de fuerza bruta, pero no se puede decir que los evite, porque se elude con VPN o con intermediarios en servidores del extranjero. Combínalo con el límite de intentos de acceso, la autenticación en dos pasos, la autenticación Basic, etc.

Con Cloudflare, todos aparecen como del mismo país

Ocurre porque, a través de una CDN, el origen de la conexión es la IP de la CDN. Recupera la IP real con set_real_ip_from y real_ip_header CF-Connecting-IP antes de hacer la detección. También puedes restringir por país con el WAF de Cloudflare.

¿Con GeoLite2-City se puede restringir hasta la ciudad?

También se pueden guardar en variables los códigos de ciudad y región, pero la precisión a nivel de ciudad es menor que la de país y es fácil que un error de detección deje fuera a usuarios legítimos. Para el control de acceso, lo prudente es quedarse en el nivel de país.

Resumen

  • La restricción por país actual en Nginx se hace con GeoIP2 (ngx_http_geoip2_module + .mmdb de GeoLite2), y los datos se actualizan con geoipupdate.
  • Guarda el código de país en una variable con geoip2, decide permitir o denegar con map y devuelve return 403 en los location de wp-login.php y /wp-admin/.
  • Si hay una CDN de por medio, recupera primero la IP real, y usa la restricción por país como medida de apoyo junto con la autenticación en dos pasos y similares.

Artículos relacionados

Referencias: ngx_http_geoip2_module (GitHub), MaxMind GeoLite2 Free Geolocation Data, MaxMind – Updating Databases (geoipupdate), Nginx ngx_http_realip_module

Historial de actualizaciones: 2026-11-09 revisión completa (se sustituye el antiguo procedimiento de GeoLite, cuya distribución terminó, por el de GeoIP2 y geoipupdate, y se añaden un ejemplo de restricción del panel de administración de WordPress y las precauciones con la CDN). Este artículo lo publica WordPress Hakase (Lightscend Inc.).

¿Dudas sobre WordPress? Quien publica este sitio está en Japón y responde en japonés. Contacto (en japonés)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲