Брутфорс-атаки на WordPress могут перегрузить сайт, даже если пароль надёжен. Рассказываем, как настроить Nginx так, чтобы wp-login.php не стал слабым местом и сервер (VPS) оставался живым.
Введение
Представьте, что вы заходите на свой WordPress-сайт, а он еле дышит. Причина может быть не в вас, а во внезапной атаке ботов. В Интернете периодически прокатываются волны брутфорс-атак на WordPress. Тысячи автоматических ботов пытаются подобрать пароль, штурмуя стандартную страницу авторизации wp-login.php. Даже если пароль надёжен и взлом не грозит, такие атаки всё равно вредят сайту.
В статье объяснили, как правильно настроить rate limiting в конфигурации Nginx для файла wp-login.php в WordPress. Пошагово разобрали, как с помощью стандартного модуля Nginx ограничить число запросов к странице логина от одного IP-адреса.
Зачем ограничивать доступ к wp-login.php
Страница wp-login.php — стандартный вход в админку WordPress. Именно она часто становится мишенью для перебора паролей. Даже без взлома атака способна положить сайт, ведь каждое обращение загружает ядро, плагины и базу, сервер быстро перегружается и перестаёт отвечать.
Чтобы избежать этого, достаточно ограничить частоту обращений к wp-login.php через Nginx. Вы задаёте порог запросов с одного IP, и всё лишнее блокируется ещё до запуска PHP. Это снимает нагрузку и останавливает бот-сети, не мешая обычным пользователям, ведь никто не кликает «Войти» десятки раз в минуту.
Важно лишь подобрать разумный лимит. Слишком строгие правила могут ударить по сотрудникам за общим IP, а слишком мягкие не остановят атаки. Ограничение частоты — не единственный метод защиты: помогают плагины, reCAPTCHA, сокрытие логина. Но именно решение на уровне Nginx лучше всего разгружает сервер.
Настройка rate limiting в конфигурации Nginx
Предположим, у вас уже настроен сайт на Nginx и вы имеете доступ к его конфигурационным файлам. В примерах будем редактировать файлы на сервере Linux (Ubuntu/Debian). Пути могут отличаться в вашей системе, но суть останется той же. Перед началом работы убедитесь, что у вас есть права доступа для изменения конфигурации Nginx, например, через root или sudo. И как всегда при правках серверных настроек, желательно сделать бекап конфигов.
Определение зоны и скорости запросов
Откройте основной конфигурационный файл Nginx. Внутри секции http { ... } нужно объявить специальную зону для отслеживания запросов и задать скорость, с которой разрешено принимать запросы из одной зоны. Добавьте в блок http строку:
set_real_ip_from <IP_или_подсеть_CDN>;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=15r/m;
Эта директива создаёт зону login_limit размером 10 Мб для отслеживания запросов по IP-адресам. Каждый IP хранится отдельно, лимит 15 обращений в минуту. Если частота выше, лишние запросы блокируются.
10 Мб памяти хватает примерно на 160 000 адресов, этого более чем достаточно. Порог в 15r/m считается оптимальным. Он срезает брутфорс, но не мешает обычному пользователю ввести пароль несколько раз подряд. При желании значения можно изменить от мягких 30r/m до жёстких 5r/m.
Если вы хотите максимально строго отсекать лишние попытки, можно указать лимит и в запросах в секунду. Например, rate=1r/s означает 1 запрос в секунду, по сути то же, что 60r/m. Формат r/m зачастую удобнее для малых значений. А вот дробные значения напрямую указать нельзя. Вместо этого можно задать, например, 30r/m, что эквивалентно одному запросу каждые 2 секунды. Важно понимать, что Nginx воспринимает этот параметр именно как скорость потока, а не как количество и интервал сброса. 15r/m — это не «15 запросов можно сразу, а потом минута пауза». Это именно сглаженный лимит, сервер пропускает в среднем 15 запросов за любой интервал в 60 секунд, распределяя их во времени.
Применение лимита к странице wp-login.php
После того как зона и скорость лимита определены, нужно сказать Nginx, где именно их использовать. Откройте конфигурацию вашего сайта. Зачастую это файл в директории /etc/nginx/sites-available/ или /etc/nginx/conf.d/, название может быть наподобие вашего домена или default. Найдите секцию server { ... }, отвечающую за ваш WordPress-сайт. Внутри неё добавьте новый location, специально для /wp-login.php.
Если в конфигурации уже есть блок, обрабатывающий PHP-файлы, важно, чтобы ваш более специфичный location для wp-login.php был размещён выше общего PHP-блока. В Nginx более точное соответствие приоритетнее. Мы будем использовать location = /wp-login.php, точное совпадение пути, чтобы применить к нему особые правила. Добавьте в ваш server блок такой фрагмент:
location = /wp-login.php {
limit_req zone=login_limit burst=3 nodelay;
limit_req_status 429;
limit_req_log_level warn;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Здесь директива limit_req zone=login_limit; включает ограничение частоты для запросов к wp-login.php. Все превышения будут отклоняться кодом 503, если не настроить иначе.
Остальной блок — это стандартная схема обработки PHP. Директива fastcgi_pass отправляет запросы в PHP-FPM через сокет, в примере php7.4-fpm.sock, у вас путь и версия могут отличаться. Важно скопировать конфигурацию из вашего общего блока location ~ \.php$, чтобы не потерять нужные параметры: fastcgi_param SCRIPT_FILENAME, include fastcgi_params, а также возможные буферы, таймауты и index.php. Без этих строк wp-login.php может вернуть 404 или 502.
В итоге блок для wp-login.php фактически повторяет обычный PHP-блок, но дополнен лимитом на частоту запросов.
Настройка параметров очереди и кода ошибки
Можно расширить директиву limit_req дополнительными параметрами burst и nodelay, а также глобально задать код ошибки через limit_req_status. Что это значит и нужно ли?
Параметр burst определяет, сколько сверхлимитных запросов можно временно удержать в очереди. По умолчанию burst равен 0, то есть ни один запрос сверх установленной скорости не задерживается, он сразу отклоняется. Если указать, например, limit_req zone=login_limit burst=5, то Nginx позволит временно накопить до 5 лишних запросов от IP, обслуживая их с задержкой. При кратковременном всплеске запросов они не будут сразу отброшены, а просто выполнятся чуть медленнее. Однако для защиты от брутфорса очередь не нужна. Нет смысла задерживать атаки, лучше сразу отказывать. Поэтому большинство администраторов либо вообще не указывают burst, что эквивалентно 0, либо ставят совсем маленький размер и включают флаг nodelay. Флаг nodelay означает «не ждать, а сразу отклонять, если лимит превышен». Поэтому если и использовать burst, то вместе с nodelay. Например:
limit_req zone=login_limit burst=3 nodelay;
Такой вариант позволил бы чуть-чуть сгладить очень короткие пачки запросов. Но, повторимся, эти настройки не обязательны. Для простоты вы можете вообще не указывать burst и nodelay, как мы сделали изначально, тогда любое превышение лимита приведёт к отказу.
Полезно задать код ответа для заблокированных запросов. По умолчанию limit_req возвращает 503 Service Unavailable. Рабочий вариант, но не самый наглядный. Часто используют 429 Too Many Requests, который прямо указывает на превышение лимита. Для этого есть директива limit_req_status. Её можно прописать в блоке http {} для всего сервера или только в location = /wp-login.php.
Есть и другие варианты. Код 444 заставляет Nginx молча закрывать соединение, что удобно против ботов, но пользователю будет непонятно, почему страница не открылась. С 429 всё яснее, к тому же можно настроить собственную страницу ошибки через error_page 429 /limited.html;.
Перезапуск Nginx и проверка
После внесения изменений в конфигурацию нужно их применить. Сохраните файлы и выполните команду проверки синтаксиса:
nginx -t
Если в выводе увидите syntax is ok, значит опечаток и ошибок нет. При наличии ошибок внимательно прочитайте, что пишет Nginx. Возможно, где-то допущен лишний символ или указан неверный путь. Устраните проблемы и повторно запустите проверку. После успешной проверки перезагрузите Nginx командой:
sudo service nginx reload
Самый простой способ проверить, работает ли лимитирование запросов, имитировать быстрые запросы к wp-login.php и посмотреть, как реагирует сервер. Можно попробовать в браузере несколько раз подряд быстро обновить страницу входа или повторно отправить форму с неправильным паролем. После определённого количества попыток вы должны увидеть отказ сервера, ошибку. Это и есть признак срабатывания лимитера.
Проверить работу лимита удобнее всего в логах Nginx. Откройте access.log и отправьте десятки запросов к wp-login.php через ApacheBench или цикл с curl. В логе успешные обращения будут отмечены кодом 200 или 302, а блокировки кодом 503 или 429 в зависимости от настройки. В error.log может появиться строка вроде limiting requests, excess: X by zone 'login_limit', что подтверждает срабатывание защиты.
Если же отказ получают даже единичные попытки входа, значит лимит применён слишком широко или установлен слишком жёсткий rate.
Исключения
В некоторых случаях может понадобиться не применять ограничение к определённым адресам. Например, вы сами регулярно заходите под одним статичным IP и не хотите ощущать никаких лимитов, даже если вводите пароль много раз. Или ваш внешний сервис мониторинга проверяет доступность wp-login.php и не должен блокироваться. Для таких ситуаций существует возможность настроить исключения по IP.
В Nginx это делается с помощью директив map и небольшой хитрости. Идея в том, чтобы подменять ключ $binary_remote_addr на пустой для некоторых адресов. Если ключ пустой, Nginx не будет ограничивать запросы. Для этого в секции http до или рядом с объявлением limit_req_zone добавьте блок map, а limit_req_zone перенастройте на новую переменную.
В конфиге (в http {}) вместо прямого $binary_remote_addr объявите новую переменную, скажем, $limit_ip. Сначала создайте map:
map $remote_addr $limit_ip {
default $binary_remote_addr;
123.45.67.89 "";
98.76.54.32 "";
}
Здесь 123.45.67.89 и 98.76.54.32 — примеры IP-адресов, которые вы хотите добавить в белый список. Для них вы явно задаёте значение "" (пустая строка). Для всех остальных переменная $limit_ip будет равна обычному $binary_remote_addr, то есть IP клиента, как раньше.
Затем измените определение зоны:
limit_req_zone $limit_ip zone=login_limit:10m rate=15r/m;
Теперь ключом для подсчёта запросов служит ваша переменная $limit_ip. Для IP, указанных в map, $limit_ip станет пустой строкой. Запросы с пустым ключом не учитываются в хранилище и не ограничиваются. Иными словами, запросы с адресов из белого списка не попадут под rate limiting. При этом важно, чтобы пустое значение было именно "", как в примере, иначе Nginx не поймёт. Добавлять IP можно списком в блоке map, по одному на строку. Также можно указывать диапазоны через маски. В простых случаях достаточно перечислить несколько адресов.
Остальные части настройки остаются прежними. В location /wp-login.php ничего менять не нужно, он уже ссылается на зону login_limit, а та теперь использует новую логику. После добавления map не забудьте перезагрузить Nginx.
Учтите: добавлять в белый список чужие динамические IP бессмысленно, только снизите защиту. Лучше включить туда один-два своих стабильных адреса. Если IP изменился, придётся обновить конфиг. Впрочем, как мы отмечали, обычной работе лимит и так не мешает, так что whitelist служит скорее перестраховкой для особо важных случаев.
Заключение
Теперь ваш сайт не будет покорно обрабатывать сотни ложных попыток входа. Вместо этого злоумышленники получат от ворот поворот после первых же запросов сверх нормы. Вы, скорее всего, даже не заметите работу этого механизма, зато в критический момент, когда боты решат пошуметь, сервер останется бодрым и невредимым.
Читайте в блоге:
- Как защитить wp-login.php в Nginx от брутфорс-атак
- Резко возросла нагрузка на хостинг в WordPress: причины и решение
- Как ускорить WordPress с кешированием: установка и настройка плагинов