IPv6 давно стал «обычной реальностью»: браузеры и провайдеры охотно используют AAAA-записи, а серверы всё чаще работают в режиме dual-stack (IPv4 + IPv6). Поэтому IP-ограничения, настроенные только «под IPv4», регулярно дают ложное чувство защищённости.
Зачем вообще думать про IPv6 в allow/deny
Ограничение доступа по IP в Nginx (директивы allow/deny) — простой и очень рабочий слой защиты для закрытых зон: админки CMS, панели мониторинга, приватные API, служебные локации. Проблема в том, что многие правила пишут под IPv4 и забывают про IPv6.
Если у сервера включён dual-stack, запрос в «закрытую» админку может прийти по IPv6. Тогда IPv4-правила не помогут, и приватная зона внезапно окажется доступной «снаружи».
Ещё нюанс: у клиентов IPv6 часто динамический, и фиксировать «один адрес» бывает рискованно — можно легко заблокировать самого себя. В таких случаях логичнее опираться на подсети (обычно /64) и держать резервный барьер вроде auth_basic.
Как Nginx применяет allow/deny: порядок и логика
Директивы allow и deny относятся к модулю ngx_http_access_module. Они сравнивают IP клиента и либо пропускают запрос, либо возвращают 403.
- Проверка идёт сверху вниз в порядке, как директивы написаны в конфиге.
- Срабатывает первое совпадение.
- Если совпадений нет, то всё решает наличие финального «запрета»
deny all;. - IPv4 и IPv6 можно спокойно смешивать в одном
serverилиlocation.
Для режима «белого списка» обязательно завершайте блок строкой
deny all;. Иначе доступ получат все, кто не попал под вашиallow.
Где размещать правила: server vs location
Директивы можно ставить на разных уровнях конфигурации, но чаще всего оптимально ограничивать доступ именно в location — так вы не рискуете «уронить» публичную часть сайта.
server— общий контур для всего виртуального хоста.location— точечная защита конкретных URI (например,/admin/,/metrics,/internal/).

IPv6 в allow/deny: синтаксис и примеры
Nginx понимает IPv6-адреса и подсети в CIDR-нотации.
- Один адрес:
allow 2001:db8:1234::10; - Подсеть /64:
allow 2001:db8:1234:5678::/64; - Локальный loopback:
allow ::1;
Шаблон для защиты админки: разрешаем IPv4, IPv6-подсеть и доступ с localhost, остальным запрещаем.
location /admin/ {
allow 192.0.2.10;
allow 2001:db8:1234:5678::/64;
allow 127.0.0.1;
allow ::1;
deny all;
proxy_pass http://backend;
}
Почему часто выбирают именно /64: у домашних/офисных сетей адрес конкретного устройства может меняться, а префикс /64 обычно остаётся стабильным в рамках выдачи провайдера/роутера.
Что выбрать для IPv6: один адрес или подсеть
- Один адрес — хорошо для серверов и внешних сервисов (например, выход вашего VPN или IP другого бэкенда).
- Подсеть — практичнее для офисов/домашних провайдеров, где «хостовая часть» IPv6 меняется.
Если сомневаетесь в стабильности префикса, не делайте ставку только на IP-ACL: добавьте второй барьер (например, auth_basic), а доступ по сети используйте как «сужение поверхности атаки».
Комбинация allow/deny и auth_basic (Basic Auth + htpasswd)
Популярный рабочий вариант: режем «весь интернет» по IP и одновременно требуем логин/пароль через Basic Auth. Сценарий особенно полезен, когда админка нужна нескольким людям из разных мест: ACL оставляет доступ только доверенным сетям, а auth_basic добавляет настоящую аутентификацию.
location /admin/ {
allow 192.0.2.0/24;
allow 2001:db8:abcd:100::/64;
allow 127.0.0.1;
allow ::1;
deny all;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd-admin;
proxy_pass http://backend;
}
Файл auth_basic_user_file храните вне веб-корня и с правами, исключающими чтение посторонними. Если используете утилиту htpasswd, убедитесь, что выбираете современный хеш (в зависимости от дистрибутива и политики безопасности).
Что проверяется раньше: IP или пароль
- Если IP запрещён, клиент сразу получит 403 и даже не увидит запрос учётных данных.
- Если IP разрешён, Nginx запросит логин/пароль и при ошибке вернёт 401.
Это удобно: меньше попыток перебора пароля и меньше лишней нагрузки.
Типовые ошибки с IPv6 в allow/deny и как их быстро найти
Ошибка 1. Разрешили IPv4, забыли IPv6
Классика: настроили allow для IPv4, добавили deny all;, проверили с одной сети — а потом выяснилось, что часть клиентов ходит по IPv6 и упирается в неожиданный доступ или неожиданный запрет (в зависимости от того, как именно был собран блок правил).
Практика: если зона должна быть закрыта, думайте в терминах dual-stack и тестируйте оба стека.
Ошибка 2. Неправильный префикс (слишком широко или слишком узко)
/64 часто подходит для «клиентских» сегментов, но провайдер может выдавать /56, /60 или даже единичный /128. Неверный префикс приводит либо к само-блокировке, либо к излишне широкому доступу.
Если вы поднимаете инфраструктуру на сервере и вам нужен предсказуемый доступ извне, иногда удобнее иметь выделенный узел (VPN/bastion) на VDS с фиксированными IPv4/IPv6 и разрешать доступ к админке только с него.
Ошибка 3. Nginx видит не реальный IP (прокси, CDN, балансировщик)
Если Nginx стоит за прокси/балансировщиком, то allow/deny будут сравнивать не реального клиента, а адрес ближайшего узла. В результате ACL либо «разрешает всем», либо «запрещает всем».
Решение — настраивать получение реального IP через real_ip_header и доверенные источники в set_real_ip_from. Доверяйте только вашим прокси и балансировщикам.
Не задавайте доверие слишком широко. Например,
set_real_ip_from 0.0.0.0/0(и аналогично широкие IPv6-диапазоны) позволяют подделать заголовок и обходить ACL.
Ошибка 4. Неполное закрытие дерева /admin/ из-за location-матчинга
Админка часто состоит из нескольких путей: /admin/, /admin/api/, /admin/assets/. Если ограничения стоят не там, где реально обрабатываются запросы (или часть попала в более специфичный location без ACL), можно получить утечки.
Правило: проверьте, какие именно location выигрывают при матчинге, и закройте все чувствительные эндпоинты.

Рецепты: защита admin panel по IPv4/IPv6
Рецепт 1. Белый список + deny all (самый строгий)
location ^~ /admin/ {
allow 198.51.100.0/24;
allow 2001:db8:10:20::/64;
allow 127.0.0.1;
allow ::1;
deny all;
auth_basic "Admin";
auth_basic_user_file /etc/nginx/.htpasswd-admin;
}
Подходит, когда админка должна быть доступна только из определённых сетей (офис, VPN, служебные серверы) и никак иначе.
Рецепт 2. Разрешить всем, но закрыть опасные точки по IP
Если публичная часть приложения должна быть доступна всем, часто имеет смысл закрыть диагностические URL (метрики, дебаг, внутренние статусы):
location = /metrics {
allow 127.0.0.1;
allow ::1;
allow 192.0.2.10;
allow 2001:db8:1234:5678::10;
deny all;
proxy_pass http://backend;
}
Рецепт 3. Раздельные правила для разных зон (UI и API)
location ^~ /admin/ {
allow 203.0.113.0/24;
allow 2001:db8:aaaa:100::/64;
deny all;
auth_basic "Admin";
auth_basic_user_file /etc/nginx/.htpasswd-admin;
}
location ^~ /admin/api/ {
allow 203.0.113.10;
allow 2001:db8:aaaa:100::10;
deny all;
}
Так вы не раздаёте доступ шире необходимого: UI — офису/VPN, API — только конкретному сервису.
Проверка и отладка: как убедиться, что IPv6 ACL работает
1) Проверьте, слушает ли Nginx IPv6
Включён ли IPv6 на уровне сокета, зависит от listen (например, listen [::]:443 ssl;). Если IPv6 не слушается, «обхода по IPv6» не будет, но и доступ по IPv6 у клиентов работать не станет.
ss -lntp | grep nginx
2) Посмотрите, какой IP видит Nginx
Для диагностики полезно временно вывести $remote_addr в access log (или использовать отдельный формат логов). Важно убедиться, что при заходе по IPv6 в $remote_addr действительно IPv6 клиента, а не адрес прокси.
Если вы работаете с NAT64/DNS64 или смешанными сетями, полезно понимать, как именно у вас формируется путь клиента до сервера. Для более глубокого контекста можно заглянуть в материал про NAT64/DNS64 и особенности доступа по IPv6.
3) Тесты curl по IPv4 и IPv6
С хоста, который должен иметь доступ, проверьте оба стека:
curl -I -4 https://example.com/admin/
curl -I -6 https://example.com/admin/
4) Проверяйте итог по статусам: 401 vs 403
- 403 — IP запрещён (сработал
deny). - 401 — IP разрешён, но Basic Auth не пройден.
- 200/302 — доступ есть, аутентификация пройдена (или не включена).
Практические советы по эксплуатации
Держите аварийный доступ
- Оставляйте доступ с localhost:
127.0.0.1и::1, чтобы можно было проверить через SSH и локальный curl. - Применяйте изменения только после проверки: сначала
nginx -t, затем reload. - Если у вас часто меняются внешние адреса, удобнее администрировать через VPN или bastion, чем постоянно править allowlist.
Не превращайте allowlist в «вечную помойку»
Списки allow разрастаются: временные подрядчики, старые офисы, «на недельку открой». Это снижает ценность ACL.
Минимальная дисциплина: комментарии к правилам (кто/зачем/до какого числа) и периодическая чистка.
IPv6: адресов много, но контроль нужен
В IPv6 нормально получать широкие префиксы. Поэтому «разрешим один адрес» часто ломается, а «разрешим слишком широкую сеть» опасен. Обычно баланс даёт связка:
- ACL по сети там, где это оправдано (офис/VPN/серверы).
auth_basicкак второй рубеж.- Корректный real IP, если вы за прокси.
Если хотите глубже разобраться, почему у клиентов адреса «плавают» и как устроены SLAAC/DHCPv6, пригодится статья про SLAAC, DHCPv6 и маршрутизацию IPv6.
Чек-лист перед выкладкой в прод
- В конце ACL для «белого списка» стоит
deny all;. - IPv6-правила добавлены (или IPv6 осознанно не слушается в
listen). - Разрешены
127.0.0.1и::1для аварийного доступа. - Если есть прокси/CDN — настроены
real_ip_headerи корректныеset_real_ip_from. - Проверены ответы по
curl -4иcurl -6. - Понимаете разницу 401 (Basic Auth) и 403 (ACL).
Итог
allow/deny в Nginx остаётся отличным инструментом, но в 2025 году его нельзя считать корректно настроенным, пока вы не проверили IPv6. Для реальной защиты административных зон чаще всего достаточно связки IP-ACL + auth_basic с htpasswd: она одновременно ограничивает круг источников и даёт аутентификацию. Главное — тестировать оба стека (IPv4/IPv6) и не забывать про реальный IP за прокси.


