Если вы всё ещё думаете про «nginx GeoIP» как про файл GeoLite2-Country.dat на диске и строку geoip_country в конфиге, 2026 преподносит сюрприз: этот формат исчез из актуального модуля, а замена стала чище. В nginx 1.31.7 (mainline, 14 октября 2026) модули ngx_http_geoip_module и ngx_stream_geoip_module теперь читают базы в формате MaxMind DB (MMDB) напрямую - в том же формате, что MaxMind годами отдаёт как стандартный для линейки GeoIP2 и GeoLite2.[1] Никакой обвязки и стороннего динамического модуля для базового случая не нужно.

Это продолжение нашего гайда про блокировку AI-скрейперов через nginx, который разбирает user-agent сторону борьбы с ботами. Здесь - другая ось: откуда пришёл запрос. Страновое блокирование и гео-роутинг - это то, как вы режете бот-нагрузку, которая вообще не представляется, соблюдаете юрисдикционные требования и отправляете пользователей на нужный региональный эндпоинт.

Зачем гео-блок в 2026

Геолокация на уровне страны решает задачи, которые не покрывает матчинг user-agent, и в 2026 три причины выглядят иначе, чем десять лет назад.

Фильтрация AI-скрейперов и бот-нагрузки по стране-источнику. Многие флоты AI-краулеров и скрапинг-операции крутятся с небольшого числа облачных и хостинговых сетей, сконцентрированных в нескольких странах. Если в аналитике подавляющая доля абьюзивного трафика идёт из сетей, которым вы всё равно не будете легитимно отдавать, страновой блок на edge срезает эту нагрузку до того, как она коснётся приложения - это заметно дешевле, чем rate-limit на тысячи отдельных IP. return 444 (или geo-осознанный rate-limit) по стране-источнику дополняет защиту от AI-скрейперов по user-agent, потому что трафик, который подделывает браузерный UA, всё равно приходит из этих сетей.

Комплаенс (GDPR и юрисдикционные правила). Иногда «кому можно доступ» - вопрос юридический, а не технический. GDPR и отраслевые требования могут заставлять ограничивать контент конкретными территориями или отказываться обрабатывать данные из юрисдикций, с которыми у вас нет договорённостей. Страновой роутинг - грубый, но эффективный инструмент: данные буквально не доходят до приложения.

Региональный спам и брут-форс. Брут-форс логинов, credential-stuffing и комментарный спам сильно регионализованы. Блокировка целых стран на конкретных эндпоинтах - или более жёсткое ограничение для них - срезает заметную долю такого шума на edge, не трогая здоровый трафик.

Честная оговорка сразу: гео-блок - это не граница безопасности. Геолокация опирается на зарегистрированные IP-диапазоны, её можно обойти VPN или арендованным IP в другой стране, а сам MaxMind прямо пишет, что IP-геолокация неточна.[2] Относитесь к этому как к инструменту формирования трафика и снижения поверхности, а не как к гарантии контроля доступа.

Что изменилось: .dat умер, остался только MMDB

Старый ngx_http_geoip_module читал базы в легаси-формате GeoIP DAT через отдельную библиотеку MaxMind GeoIP Legacy C. Это та самая пара .dat/.dat.bak, которую вы могли помнить. Модуль поддерживает MMDB начиная с 1.31.7, в дополнение к легаси-DAT, и документация теперь показывает пример конфигурации geoip_country GeoLite2-Country.mmdb;.[3]

Разница форматов важна для того, что вы скачиваете:

  • DAT (GeoIP legacy) - формат старых баз GeoLite / GeoIP. Требовал библиотеку GeoIP Legacy C (geoip-api-c) и тот самый формат, который MaxMind фактически снял с актуальных загрузок.
  • MMDB (MaxMind DB) - формат баз GeoIP2 и GeoLite2, то есть всего, что MaxMind распространяет сегодня. Это собственный бинарный формат, «разработанный для быстрых просмотров IP-адресов», его спецификация открыта и опубликована, читает его C-библиотека libmaxminddb.[4][5] Привязки для динамических языков к тому же формату питают GeoIP2-клиентские библиотеки.

Конкретно для 2026: базы GeoLite2, которые вы скачиваете из своего аккаунта MaxMind, поставляются как .mmdb (бинарный, для быстрых просмотров в масштабе) или .csv (для импорта в БД).[6] Файлов .dat, которые ждал старый GeoIP C API, в продукте больше нет. Поэтому практическая миграция проста: перестаньте искать .dat, указывайте конфиг на .mmdb.

Предусловие: libmaxminddb

Для баз MMDB geoip-модулю нужна библиотека libmaxminddb на этапе сборки; легаси-DAT вместо неё требуют GeoIP Legacy C.[3] Если вы пользуетесь пакетом дистрибутива, где уже включён --with-http_geoip_module (или stream-вариант), нужен соответствующий рантайм - на Ubuntu/Debian MaxMind держит PPA с libmaxminddb0, libmaxminddb-dev и mmdb-bin.[5] Сам модуль по умолчанию не компилируется; на сборках из исходников он включается флагами --with-http_geoip_module и --with-stream_geoip_module.[3][7]

# Ubuntu/Debian через PPA MaxMind
sudo add-apt-repository ppa:maxmind/ppa
sudo apt update
sudo apt install libmaxminddb0 libmaxminddb-dev mmdb-bin

MMDB-просмотр быстрый - в этом весь смысл формата. Но как и с любым утверждением «это быстро», мы не публикуем цифру, которую не замерили. Что документация гарантирует - это цель дизайна формата: «быстрые просмотры IP-адресов». Без собственного бенчмарка на реальном аккаунте цифру мы не вписываем, чем выдумывать.

Получить базу: GeoLite2 требует аккаунт и должна быть свежей

Бесплатные базы GeoLite2 требуют аккаунт MaxMind и лицензионный ключ - MaxMind прямо пишет, что вы регистрируетесь, а затем генерируете лицензионный ключ для аутентификации загрузок и web-сервисных запросов.[8] Голой прямой ссылки на .mmdb без credentials вы не найдёте.

Два ограничения от MaxMind формируют ваш цикл обновления:

  • Лимит загрузок: пользователи GeoLite ограничены 30 загрузками баз в день.[6]
  • Свежесть - условие лицензии: GeoLite EULA требует держать данные актуальными и удалять базы GeoLite в течение 30 дней с момента нового релиза.[6] Помимо юридического требования, устаревшая база стран - это liability: IP, переехавший между сетями, резолвится в неверную страну, и ваш блок перенаправляет реальных пользователей.

Рекомендованный способ поддерживать бинарную базу актуальной - программа GeoIP Update (geoipupdate), которая автоматически обновляет бинарные базы GeoIP и GeoLite.[9] Пропишите в её GeoIP.conf ваш AccountID и LicenseKey и запускайте из cron; она Apache-2.0/MIT и ставится из PPA или архива.[10]

# /etc/GeoIP.conf
AccountID 123456
LicenseKey YOUR_LICENSE_KEY
EditionIDs GeoLite2-Country GeoLite2-ASN

# Кладите базы туда, где их ждёт конфиг nginx
# (пакеты дистрибутива по умолчанию пишут в /usr/share/GeoIP; сборка в /usr/local - в /usr/local/share/GeoIP)

Подключаем: модуль 1.31.7

При актуальном GeoLite2-Country.mmdb на диске модуль - это две строки в контексте http:

http {
    geoip_country  /usr/share/GeoIP/GeoLite2-Country.mmdb;
    geoip_city     /usr/share/GeoIP/GeoLite2-City.mmdb;   # опционально
    geoip_org      /usr/share/GeoIP/GeoLite2-ASN.mmdb;    # опционально
}

Всё - nginx 1.31.7+ читает MMDB нативно. Только база стран даёт вам следующие переменные, описанные на странице модуля:[3]

  • $geoip_country_code - двухбуквенный код, например RU, US.
  • $geoip_country_code3 - трёхбуквенный код, например RUS, USA.
  • $geoip_country_name - название страны, например Russian Federation, United States.

База городов добавляет переменные региона, города, почтового индекса, широты/долготы и кода континента ($geoip_city_country_code, $geoip_region, $geoip_city, …); база ASN добавляет $geoip_org.[3] Если вы за прокси, geoip_proxy и geoip_proxy_recursive говорят nginx доверять X-Forwarded-For от конкретных прокси - без них модуль геолоцирует IP прокси, а не клиента.[3]

Имя, которое стоит понимать правильно. В туториалах вы встретите блок geoip2 { } с auto_reload и $geoip2_data_country_code. Это другое - популярный сторонний модуль ngx_http_geoip2_module от leev, который подвязывает MMDB-переменные через директиву geoip2.[11] Штатный модуль nginx 1.31.7 - нативный, показанный выше: geoip_country/geoip_city/geoip_org и $geoip_country_code. Оба дают просмотр на базе MMDB; модуль leev дополнительно предлагает auto_reload и кастомные имена переменных, ценой сборки как динамического модуля и самостоятельного сопровождения. Если вам нужна stock-сборка - используйте нативные директивы.

Блокируем и роутим по стране: map, if и geo-осознанный rate-limit

Чистый stock-способ превратить страновую переменную в действие - map. Вынесите решение в одно место и используйте везде:

# контекст http
geoip_country /usr/share/GeoIP/GeoLite2-Country.mmdb;

# Гео-роутинг: отправляем пользователей ЕС на региональный эндпоинт
map $geoip_country_code $region_backend {
    default  global.example.com;
    DE       eu.example.com;
    FR       eu.example.com;
    NL       eu.example.com;
}

Осторожно с паттерном allowlist: перечислять все страны, которые вы обслуживаете, и всё остальное отправлять в «block». map разрешает сначала точное совпадение строки, потом самый длинный wildcard, потом первое подходящее regexp - правила «по порядку в файле» тут нет, так что у блоклиста default 0 делает ровно одну работу, а allowlist придётся поддерживать по мере роста аудитории - классическая грабля. Блоклист проще для рассуждения:

map $geoip_country_code $is_blocked_country {
    default   0;
    KP        1;   # Северная Корея
    IR        1;
    RU        1;   # пример - берите свой список
    CN        1;
}

Затем действуйте в блоке server. Связка if + return 444 переиспользует приём из гайда про AI-скрейперов: 444 закрывает соединение без ответа, это дешевле реального 403 и идеально для отбивания ботов:

server {
    listen 443 ssl;
    server_name example.com;

    if ($is_blocked_country) {
        return 444;
    }

    location / {
        proxy_pass http://backend;
    }
}

Для гео-роутинга подставьте замапленную $region_backend в цель прокси:

location / {
    proxy_pass http://$region_backend;
}

Geo-осознанный rate-limit сочетает map с ngx_http_limit_req_module: ключите зону по производному от страны токену, чтобы целые регионы имели один bucket, а ваши хорошие регионы оставались свободными. Фокус в том, чтобы отдавать ключ только для стран, которые вы хотите троттлить: запросы с пустым ключом вообще не учитываются, поэтому флаг «0 для всех остальных» свалит весь ваш здоровый трафик в один общий bucket - ровно противоположное желаемому:[12]

# контекст http
map $geoip_country_code $throttled_country {
    default  "";     # пустой ключ -> запрос не учитывается
    KP       KP;
    IR       IR;
    CN       CN;
}

limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;
limit_req_zone $throttled_country   zone=percountry:10m rate=1r/s;

# в контексте server, рядом с блоком:
location /signin {                     # цель для брут-форса
    limit_req zone=perip      burst=20 nodelay;
    limit_req zone=percountry burst=3  nodelay;
    limit_req_status 429;
}

Семантика leaky bucket, burst, nodelay и расчёт размера зоны - те же, что в гайде про AI-скрейперов: один мегабайт держит примерно 8 тысяч состояний по 128 байт на 64-битных платформах, по одному на уникальный ключ.[12]

Stream-модуль для TCP/UDP

Геолокация - не только HTTP. Модуль ngx_stream_geoip_module (включается --with-stream_geoip_module, существует с 1.11.3) создаёт те же переменные страны/города/организации по IP клиента, и по чейнджлогу 1.31.7 он тоже поддерживает MMDB наряду с легаси-форматом.[7] Используйте его в блоке stream { }, чтобы гео-блокировать или гео-роутить не-HTTP трафик - SSH, SMTP-релей, порт БД, который вы хотите держать доступным только из своего региона:

stream {
    geoip_country /usr/share/GeoIP/GeoLite2-Country.mmdb;

    map $geoip_country_code $deny_mysql {
        default 0;
        IR      1;
        KP      1;
    }

    server {
        listen 3306;
        if ($deny_mysql) {
            return;
        }
        proxy_pass db.internal:3306;
    }
}

В stream вызов return; (без кода) закрывает TCP-соединение - это аналог отбрасывания запроса для stream. Как и с любым if, держите условие минимальным и делайте поиск через map - у stream-модуля тот же набор ограничений на if, что и у http-версии (он работает, но полной семантики rewrite-модуля внутри него ожидать нельзя).

Ограничения и операционные заметки

  • MMDB - не drop-in для синтаксиса .dat. Если вы мигрируете старый конфиг, использовавший легаси-переменные GeoIP и библиотеку GeoIP Legacy C, имена переменных модуля ($geoip_country_code и т.д.) переносятся, а вот конвейер баз - нет: вам нужно получить и обновлять файл .mmdb, а для GeoLite2 это требует аккаунта и лицензионного ключа MaxMind.[8] Старые .dat стороннего происхождения не поддерживаются.
  • В stock-модуле нет auto_reload. Нативный geoip_country читает файл; на nginx 1.31.7 нет встроенной подгрузки при изменении так, как это даёт auto_reload у модуля leev.[3][11] Либо перезапускайте/reload nginx после прогона geoipupdate, либо берите сторонний модуль, если горячая перезагрузка - жёсткое требование. Именно поэтому geoipupdate обычно парится с systemd-таймером или шагом reload после загрузки.
  • Лицензия и свежесть - реальные обязательства. С GeoLite2 вы принимаете лимит загрузок (30/день) и правило удаления в течение 30 дней.[6] Если вы перерастёте GeoLite2, платные базы GeoIP дают данные, которых нет в free-тире: имя ISP, тип соединения, домен, тип пользователя и отдельные базы Anonymous IP для детекта прокси/VPN - но это платный апгрейд, и мы ссылаемся на него лишь как на официальный путь, цитируя документацию MaxMind.[13]
  • Точность - не гарантия. MaxMind заявляет, что геолокации часто близки к населённым центрам и не должны идентифицировать конкретный адрес; бесплатный GeoLite City «заметно менее точен», чем платный GeoIP City, и не рекомендован для коммерческого использования.[2][6] Для странового блокирования бот-сетей такой гранулярности обычно достаточно - хостинговая сеть в Иране или Северной Корее геолоцируется однозначно. Но это не замена IP-allowlist для всего чувствительного.

Коротко

Nginx 1.31.7+ читает MMDB-файлы GeoLite2 нативно через geoip_country / geoip_city / geoip_org, так что конвейер .dat + GeoIP Legacy C устарел. Ваш сетап 2026: ставим libmaxminddb, скачиваем GeoLite2-Country.mmdb с аккаунтом и лицензионным ключом MaxMind, указываем директиву на файл, превращаем $geoip_country_code в действие через map (return 444 для блоклиста) и переиспользуем переменную и для гео-роутинга, и для geo-осознанного rate-limit. Stream-модуль распространяет ту же идею на TCP/UDP-порты. Держите базу актуальной через geoipupdate в cron - свежесть это и условие лицензии, и то, что держит блок точным, - и помните, что в stock нет auto_reload, поэтому после обновлений делайте reload nginx. Гео-блок - это слой формирования трафика, а не граница доступа: он снижает поверхность и бот-нагрузку, но VPN обходит страновую проверку, так что спаривайте его с реальным укреплением эндпоинтов.

Источники

  1. nginx - CHANGES 1.31.7 (14 окт 2026): «Feature: the ngx_http_geoip_module and ngx_stream_geoip_module now support databases in the MaxMind DB (MMDB) format.» https://nginx.org/en/CHANGES - checked 2026-10-18
  2. MaxMind - GeoLite Databases and Web Services («IP geolocation is inherently imprecise… locations are often near population centers… should not be used to identify a particular address or household»). https://dev.maxmind.com/geoip/geolite2-free-geolocation-data/ - checked 2026-10-18
  3. nginx - Module ngx_http_geoip_module («Since 1.31.7, the module supports databases in MaxMind DB (MMDB) format»; требует libmaxminddb для MMDB; директивы geoip_country/geoip_city/geoip_org/geoip_proxy/geoip_proxy_recursive; переменные $geoip_country_code/$geoip_country_code3/$geoip_country_name и др.; --with-http_geoip_module; пример geoip_country GeoLite2-Country.mmdb;). https://nginx.org/en/docs/http/ngx_http_geoip_module.html - checked 2026-10-18
  4. maxmind/libmaxminddb - «C library for the MaxMind DB file format… custom binary format designed to facilitate fast lookups of IP addresses… The MaxMind DB format is an open format.» https://github.com/maxmind/libmaxminddb - checked 2026-10-18
  5. MaxMind DB spec - открытая спецификация формата. https://maxmind.github.io/MaxMind-DB/ - checked 2026-10-18
  6. MaxMind - GeoLite databases («two formats: binary (.mmdb)… and .csv»; пользователи GeoLite ограничены 30 загрузками баз в день; EULA требует удалять базы GeoLite в течение 30 дней с нового релиза; «The free GeoLite City is considerably less accurate than the paid GeoIP City data, and is not recommended for commercial use cases»). https://dev.maxmind.com/geoip/geolite2-free-geolocation-data/ - checked 2026-10-18
  7. nginx - CHANGES 1.31.7 (имя модуля ngx_stream_geoip_module) и Module ngx_stream_geoip_module (1.11.3+, --with-stream_geoip_module, переменные страны/города/организации по IP клиента). https://nginx.org/en/docs/stream/ngx_stream_geoip_module.html - checked 2026-10-18
  8. MaxMind - GeoLite: «After you have a MaxMind account, you should generate one or more license keys… to authenticate database downloads or web service requests.» https://dev.maxmind.com/geoip/geolite2-free-geolocation-data/ - checked 2026-10-18
  9. MaxMind - Updating GeoIP and GeoLite Databases («For clients using our binary database format, we highly recommend using GeoIP Update, but the direct download method is available if necessary»; пример cron). https://dev.maxmind.com/geoip/updating-databases/ - checked 2026-10-18
  10. maxmind/geoipupdate - автоматическое обновление бинарных баз GeoIP и GeoLite; конфиг /etc/GeoIP.conf (или /usr/local/etc/GeoIP.conf) с AccountID/LicenseKey; Apache-2.0 или MIT; PPA на Ubuntu. https://github.com/maxmind/geoipupdate - checked 2026-10-18
  11. leev/ngx_http_geoip2_module - сторонний модуль с синтаксисом geoip2 { auto_reload …; $geoip2_data_country_code …; }; BSD 2-Clause; требует libmaxminddb; поддерживает http и stream. https://github.com/leev/ngx_http_geoip2_module - checked 2026-10-18
  12. nginx - Module ngx_http_limit_req_module (leaky bucket, burst, nodelay, limit_req_status по умолчанию 503, расчёт размера зоны: ~8 тысяч состояний по 128 байт на МБ на 64-бит). https://nginx.org/en/docs/http/ngx_http_limit_req_module.html - checked 2026-10-18
  13. MaxMind - GeoIP databases (платный тир: базы ISP, Connection Type, Domain, Anonymous IP для детекта прокси/VPN, ASN; ссылки на GeoLite-аналоги - с той же страницы-индекса). https://dev.maxmind.com/geoip/docs/databases/ - checked 2026-10-18