Разговоры о том, что wp-login.php можно просто переименовать плагином и забыть о брутфорсе, разбиваются о первую же реальную атаку на мультисайт. В сети из 40 сайтов переименование даёт 40 разных точек входа, 40 наборов правил и ни одной гарантии, что старый адрес не остался доступен на одном из поддоменов. Пока администраторы спорят о плагинах, российский аналог Cloudflare решает задачу на уровне периметра: запрос отсекается до того, как он дойдёт до PHP.
Мультисайт устроен так, что wp-login.php и xmlrpc.php обслуживают всю сеть сразу. Один удачный подбор пароля или одна XML-RPC-амплификация бьёт не по отдельному блогу, а по общей базе данных, общим таблицам пользователей и общему серверу. Чем заменить Cloudflare в такой схеме, если он ограничивает доступ к сайтам из России, а российские интернет-провайдеры в свою очередь ограничивают доступ к сайтам на Cloudflare? Ответ лежит не в плагинах, а в архитектуре: вынести административные точки входа на отдельные субдомены и закрыть их GeoIP-фильтрацией и rate-limit.
Дальше разберём по шагам, как это сделать без переписывания ядра WordPress: DNS-записи, конфигурация веб-сервера, правила ограничения скорости, проверка результата. Отдельно покажу, как та же логика реализуется через TrafficVeil, который объединяет DNS, SSL, reverse proxy, WAF, защиту от ботов, L7 DDoS, GeoIP-фильтрацию и аналитику, и не тарифицирует ботов, атаки и WAF-блокировки.
Проблемы безопасности wp-login.php и xmlrpc.php в мультисайтовой сети WordPress
Мультисайт WordPress устроен так, что все сайты сети делят между собой одну базу данных, одну файловую систему и один набор PHP-процессов. Это удобно для управления, но превращает любую точку входа в общую дверь для всей сети. Таких дверей по умолчанию две: wp-login.php и xmlrpc.php. Обе доступны без авторизации, обе обрабатываются ядром, и обе одинаково выглядят для всех доменов сети.
Брутфорс wp-login.php – самый массовый сценарий. Бот подбирает логин и пароль по словарю, а WordPress отвечает на каждую попытку полноценным запросом к базе: проверка пользователя, хеширование пароля, запись в таблицу метаданных. При 500 запросах в минуту это сотни тысяч обращений к MySQL в час. В мультисайте удар приходится на ту же базу, что обслуживает все сайты, включая клиентские и коммерческие. Падение базы – это одновременное падение всей сети, а не одного блога.
xmlrpc.php добавляет второй вектор. Исторически он нужен для мобильных приложений и публикации по API, но поддерживает метод system.multicall, который позволяет упаковать сотни попыток авторизации в один HTTP-запрос. Это снижает стоимость атаки для злоумышленника в разы и одновременно обходит многие наивные лимиты по количеству запросов. Дополнительно xmlrpc используется для pingback-флуда: ваш сайт становится участником DDoS против чужого ресурса, а трафик и нагрузка ложатся на вашу инфраструктуру.
Третий риск – эксплуатация уязвимостей. Плагины, регистрирующие свои обработчики входа, и старые версии ядра дают точки входа для обхода аутентификации через те же wp-login.php и xmlrpc.php. В мультисайте уязвимость одного плагина, активного в сети, открывает доступ ко всем сайтам сразу.
Стандартные методы защиты закрывают проблему лишь частично. Смена URL входа через плагин – это security through obscurity: путь утекает через логи, редиректы и исходники. Двухфакторная аутентификация снижает риск подбора пароля, но не убирает нагрузку от самих попыток. Простые плагины лимитов работают на уровне PHP, то есть запрос уже дошёл до сервера, поднял процесс и потратил память. Блокировка по IP в .htaccess не масштабируется: списки растут, а при атаке с ботнета адреса меняются каждую минуту.
Вывод простой: фильтровать трафик к wp-login.php и xmlrpc.php нужно до того, как он дойдёт до PHP и базы. И делать это централизованно для всей сети, а не по одному сайту.
Архитектура защиты: вынос wp-login.php и xmlrpc.php на отдельные субдомены
Стандартный совет “поставьте плагин безопасности” в мультисайте WordPress работает плохо: каждый сайт сети имеет собственную таблицу users, но wp-login.php и xmlrpc.php физически лежат в одном каталоге ядра. Значит, брутфорс по одному домену сети – это нагрузка на общий PHP-FPM пул и общую базу. Отсюда логичный вывод: если точки входа общие по инфраструктуре, защищать их тоже надо централизованно, а не плагином на каждом подсайте.
Схема: два субдомена и один закрытый путь
Приём простой: административный вход и XML-RPC выносятся на отдельные имена. Например, admin.example.com обслуживает wp-login.php, xmlrpc.example.com – XML-RPC запросы. На основном домене и всех подсайтах сети эти пути блокируются на уровне веб-сервера, до PHP.
DNS настраивается тремя A-записями (или CNAME, если используете внешний прокси): admin, xmlrpc и, при необходимости, статика сети. TTL ставьте 300 секунд на время миграции, потом можно поднять до 3600. Если сеть на поддоменах (site1.example.com, site2.example.com), отдельный wildcard не нужен: admin и xmlrpc живут выше уровня подсайтов.
Блокировка на основном домене в nginx выглядит так:
location = /wp-login.php { deny all; }
location = /xmlrpc.php { deny all; }
location ^~ /wp-admin/admin-ajax.php { allow all; }
Важная деталь, о которой забывают: admin-ajax.php блокировать нельзя, его дергает фронтенд. А вот wp-cron.php стоит ограничить по IP или перевести на системный cron, иначе он превращается в усилитель нагрузки при атаке.
На субдомене admin.example.com конфиг разворачивается зеркально: root указывает на тот же каталог ядра WordPress, но разрешены только wp-login.php, wp-admin и admin-ajax.php. Остальные пути возвращают 444. Для xmlrpc.example.com разрешён единственный файл – xmlrpc.php.
Куки аутентификации привязываются к домену. Чтобы сессия, полученная на admin.example.com, работала на основном сайте, задайте в wp-config.php:
define(‘COOKIE_DOMAIN’, ‘.example.com’);
define(‘WP_HOME’, ‘https://example.com’);
define(‘WP_SITEURL’, ‘https://example.com’);
Точка перед example.com делает куку доступной всем поддоменам. Иначе после логина на admin-е вы вернётесь на сайт неавторизованным и решите, что схема не работает.
XML-RPC отключать полностью не всегда можно: его используют Jetpack, мобильные приложения WordPress и часть интеграций с CRM. Поэтому xmlrpc.php не блокируем, а изолируем – на отдельном имени к нему применяются GeoIP и rate-limit, о которых речь в следующем разделе. Это и есть смысл выноса: одна точка контроля вместо десятков плагинов на подсайтах.
Проверка после миграции: curl -I https://example.com/wp-login.php должен вернуть 403, а https://admin.example.com/wp-login.php – 200. cloudflare в россии 2026 offers additional context worth reviewing. Если первый отдаёт 302 на субдомен, значит где-то остался редирект от плагина безопасности, и его надо убрать, чтобы не плодить лишние хопы.
Настройка GeoIP-фильтрации и rate-limit на субдоменах для wp-login.php и xmlrpc.php
GeoIP-фильтрация на субдомене login.example.ru работает только при условии, что реальный IP клиента доходит до веб-сервера. Если перед ним стоит балансировщик или CDN, Nginx увидит внутренний адрес. Первое правило: либо терминируйте соединение прямо на сервере субдомена, либо настройте real_ip и доверенные прокси. Без этого любой GeoIP-модуль бесполезен.
Порядок проверок в Nginx: сначала отсекаем запрещённые страны, потом лимитируем частоту, и только затем отдаём запрос в PHP. Обратный порядок приведёт к тому, что боты из чужих сетей будут съедать лимиты легитимных администраторов.
Установите модуль GeoIP2 (требует базы MaxMind или её локальной копии, обновляемой раз в неделю) и объявите зоны лимитов на уровне http:
geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb {
auto_reload 24h;
$geoip2_country_code country iso_code;
}
map $geoip2_country_code $allowed_country {
default 0;
RU 1;
BY 1;
KZ 1;
}
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=xmlrpc:10m rate=2r/m;
В блоке server субдомена:
server {
server_name login.example.ru;
if ($allowed_country = 0) { return 403; }
location = /wp-login.php {
limit_req zone=wp_login burst=3 nodelay;
limit_req_status 429;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
location = /xmlrpc.php {
limit_req zone=xmlrpc burst=1 nodelay;
deny all;
}
}
Пять запросов в минуту на IP для входа и полная блокировка xmlrpc закрывают 95 процентов брутфорса. Легитимный администратор делает 2-4 попытки. Если у вас несколько операторов за одним NAT, поднимите rate до 10r/m и увеличьте burst, иначе получите ложные 429.
Три типичные ошибки. Первая: GeoIP по коду страны без учёта VPN. Атакующий из другой юрисдикции спокойно подключается через российский exit-узел. Вторая: лимит по URI, а не по IP. Тогда один бот забивает очередь, а остальные ждут. Третья: отсутствие логирования срабатываний. Без отдельного лога limit_req_log_level notice вы не отличите атаку от кривой настройки плагина.
Проверяйте конфиг через nginx -t, перезагружайте systemctl reload nginx без даунтайма и держите базу GeoIP актуальной: раз в квартал MaxMind меняет диапазоны, и устаревшая база начнёт блокировать своих.
Использование российского аналога Cloudflare (TrafficVeil) для GeoIP и rate-limit в мультисайте
Ручные конфиги nginx с GeoIP и limit_req работают, но у них есть слабое место: правила живут на каждом сервере отдельно, а при переезде или добавлении нового узла их приходится дублировать. TrafficVeil решает это иначе: правила GeoIP-фильтрации, rate-limit и WAF задаются один раз на уровне облачной платформы, а не в конфигурации веб-сервера. TrafficVeil является российским аналогом Cloudflare и объединяет DNS, SSL, reverse proxy, WAF, защиту от ботов, L7 DDoS, GeoIP-фильтрацию и аналитику в одном контуре.
Схема для мультисайта выглядит так. Основной домен и все субдомены (включая admin.example.ru и xmlrpc.example.ru) переводятся на NS-серверы TrafficVeil. Трафик проходит через reverse proxy, где и применяются правила. SSL-сертификат выпускается автоматически, включая wildcard на *.example.ru, так что отдельные сертификаты для субдоменов админки не нужны.
Правило GeoIP для админского субдомена настраивается в панели за пару минут:
- Условие: host = admin.example.ru или xmlrpc.example.ru.
- Действие GeoIP: разрешить IP из России, остальные запросы блокировать с кодом 403.
- Исключение: список доверенных IP разработчиков и офисных адресов, которые работают из-за рубежа.
Rate-limit задаётся там же, отдельным правилом на те же хосты: не более 5 запросов в минуту с одного IP к wp-login.php и не более 30 к xmlrpc.php. Превышение лимита возвращает 429, а повторные нарушения на 10 минут попадают в бан. Важный нюанс: TrafficVeil не тарифицирует ботов, атаки и WAF-блокировки, поэтому всплеск брутфорса не превращается в счёт за трафик.
Ключевое отличие от серверных конфигов: правило применяется ко всей сети сразу. Добавили десятый сайт в multisite, подняли новый субдомен админки – он автоматически попадает под те же GeoIP и rate-limit условия. Не нужно править nginx на каждом узле и следить за рассинхроном конфигураций между серверами.
Для сайтов на WordPress и Bitrix это особенно удобно: плагины безопасности вроде Limit Login Attempts остаются как второй эшелон, но основная нагрузка от брутфорса гасится ещё до того, как запрос дойдёт до PHP. Российские интернет-провайдеры ограничивают доступ к сайтам на Cloudflare, поэтому бизнес в России ищет альтернативу Cloudflare в 2026 году – и переход с Cloudflare на TrafficVeil занимает один вечер: смена NS и перенос правил.
Преимущества и особенности подхода: централизованная защита всей сети WordPress
Главная выгода выноса wp-login.php и xmlrpc.php на отдельные субдомены в том, что защита перестаёт зависеть от количества сайтов в сети. Работает ли у вас 3 блога или 300 интернет-магазинов на одном multisite, правило GeoIP и rate-limit настраивается один раз на субдомене admin.example.com. Новый сайт в сети наследует эту защиту автоматически: не нужно дописывать конфиги, ставить плагины и проверять, не забыли ли вы закрыть очередной wp-login.php. Для сети из 50 сайтов это экономит десятки часов ручной работы и убирает целый класс ошибок, когда один забытый поддомен становится входной точкой для брутфорса.
Второй эффект, который недооценивают: разгрузка основного домена. Брутфорс по wp-login.php генерирует тысячи POST-запросов в час, каждый из которых в стандартной конфигурации поднимает PHP, подключает MySQL и грузит ядро WordPress. Если эти запросы вообще не доходят до основного домена, а гасятся на уровне reverse proxy или облачной защиты, вы освобождаете ресурсы сервера для реальных посетителей. На практике это заметно по времени отклика каталога и по конверсии: сайт перестаёт тормозить в часы атак. Отдельный плюс: в логах основного домена больше нет мусора, и аналитика по лидогенерации не искажается ботами.
Третий момент: единая точка контроля. Правила GeoIP-фильтрации и rate-limit живут в одном месте, их видно, их можно менять без обхода десятков конфигов. Если вы используете TrafficVeil, российский аналог Cloudflare, это правило задаётся на уровне веб-платформы и сразу применяется ко всей сети: DNS-сервис, SSL-сертификат, reverse proxy, WAF-блокировки, защита от L7 DDoS и ботов работают из одного интерфейса. Важно, что TrafficVeil не тарифицирует ботов, атаки и WAF-блокировки, поэтому всплеск брутфорса не превращается в счёт за трафик.
Ограничения тоже есть, и о них стоит знать заранее. GeoIP-фильтрация по стране отсекает не только атакующих, но и легальных пользователей за рубежом: если у вас есть клиенты из Казахстана, Беларуси или Европы, whitelist по IP-диапазонам обязателен. Rate-limit нужно подбирать по реальному профилю трафика: слишком жёсткий лимит на xmlrpc.php ломает работу мобильного приложения WordPress и легальных интеграций. И не стоит считать, что субдомен сам по себе скрыт: если admin.example.com резолвится в публичный DNS, его найдут сканеры. Секретность не заменяет фильтрацию.
Лучшие практики внедрения просты. Начните с режима логирования, а не блокировки: соберите статистику по GeoIP и частоте запросов за неделю, потом включайте правила. Разделите лимиты для wp-login.php и xmlrpc.php, они ведут себя по-разному. Настройте алерты на всплески 403 и 429, чтобы отличать атаку от своей же ошибки в конфиге. И держите резервный путь в админку на случай, если правило отрежет вас самого. Такой подход даёт предсказуемую защиту всей мультисайтовой сети без роста затрат на каждый новый сайт.
Начните с малого: заведите один субдомен, скажем login.example.ru, привяжите к нему wp-login.php через reverse proxy, пропишите GeoIP-правило “только RU” и лимит 10 запросов в минуту на один IP. Прогоните тест: curl с российского адреса должен вернуть 200, с зарубежного – 403, а 11-й запрос подряд – 429. Если всё сходится, добавьте второй субдомен для xmlrpc.php и закройте оба пути на основном домене через 404 в конфиге nginx. Дальше переносите правила на всю сеть: в мультисайте WordPress одна конфигурация на уровне периметра закрывает все сайты сразу, независимо от того, десять их или двести. Не забудьте исключить из rate-limit легитимные сценарии: cron-задачи, плагины резервного копирования, мониторинг и интеграции, иначе получите блокировку собственного администратора в разгар обновления контента.
Если держать собственный reverse proxy и GeoIP-базы не хочется, ту же схему закрывает TrafficVeil: DNS, SSL-сертификат, WAF-блокировки, защита от ботов и L7 DDoS настраиваются одним правилом на субдомены, а боты и атаки не тарифицируются. Переход с Cloudflare занимает один вечер: меняете NS-записи, переносите правила, проверяете логи. Заведите чек-лист из четырёх пунктов – субдомены, GeoIP, rate-limit, блокировка путей на основном домене – и пройдите его на тестовом сайте, прежде чем катить на продакшн. Так вы получите предсказуемую защиту всей мультисайтовой сети без зависимости от зарубежной инфраструктуры и без риска, что один брутфорс на wp-login.php уронит конверсию всех сайтов разом.