Веб-серверы и Nginx 8 мин чтения 2026-09-07

WordPress грузит VDS на 100%: блокировка атак на wp-login.php и xmlrpc.php, тюнинг Nginx и кэш без тяжелых плагинов

Сайт на WordPress внезапно уложил процессор VDS в 100%, а PHP-FPM забил всю память? Разбираем реальные причины: отсекаем паразитный брутфорс wp-login.php и xmlrpc.php на уровне Nginx, настраиваем Fail2ban и включаем микрокэширование FastCGI.

Инженерная редакция SysKit Проверено инженерами SysKit.ru

Типичная ночная тревога сисадмина или вебмастера: безобидный сайт клиента на WordPress с посещаемостью 300 человек в сутки внезапно укладывает процессор 2-ядерного VDS в 100%, воркеры PHP-FPM утилизируют всю доступную оперативную память, а MySQL аварийно завершается системным демоном OOM Killer. Веб-сервер встречает посетителей ошибками 502 Bad Gateway или 504 Gateway Time-out.

В 9 из 10 случаев причина кроется вовсе не во внезапном вирусном наплыве покупателей. Сайт атакует распределенный ботнет, непрерывно подбирающий пароли к административной панели через файл wp-login.php или отправляющий лавину POST-запросов к устаревшему интерфейсу xmlrpc.php. Установка «тяжелых» защитных плагинов вроде Wordfence или плагинов кэширования лишь усугубляет проблему: PHP тратит драгоценные мегабайты памяти на инициализацию защиты при каждом запросе бота.

💡 Инженерный принцип SysKit

Защита и оптимизация высоконагруженного стека должны выполняться на уровне инфраструктуры (Nginx и сетевого фильтра OS), а не кодом PHP. Отсечение паразитных запросов на веб-сервере Nginx требует десятых долей миллисекунды и практически нулевых затрат CPU, полностью освобождая PHP-FPM и MySQL для обслуживания реальных посетителей.

1. Экспресс-диагностика: кто грузит VDS — посетители или ботнет

Прежде чем вносить изменения в конфигурационные файлы, локализуем источник аномальной нагрузки в терминале вашего сервера. Подключитесь по SSH и проверьте текущее распределение ресурсов:

# Проверяем потребление CPU и памяти процессами PHP-FPM
htop

# Смотрим общее количество активных воркеров пула PHP-FPM
ps aux | grep php-fpm | grep -v grep | wc -l

Если десятки воркеров PHP-FPM непрерывно висят в состоянии R (running) со 100% утилизацией процессора, откройте журнал доступа Nginx и отфильтруйте самые запрашиваемые URI за последние несколько минут:

# Топ-15 запрашиваемых адресов в access.log Nginx
tail -n 2000 /var/log/nginx/access.log | awk '{print $7}' | cut -d'?' -f1 | sort | uniq -c | sort -rn | head -15

Если в верхних строчках вы видите сотни обращений к /xmlrpc.php или /wp-login.php, сервер находится под направленным брутфорс-сканированием.

Почему это фатально для WordPress? Каждый POST-запрос к wp-login.php запускает полный цикл инициализации ядра (bootstrap), подключение десятков активных плагинов и ресурсоемкую функцию wp_check_password(), выполняющую криптографическое хэширование через bcrypt/phpass с намеренно высокой вычислительной сложностью. Всего 15–20 одновременных запросов от ботов способны полностью парализовать сервер с 1–2 ГБ RAM.

2. Отключение и полная блокировка xmlrpc.php на Nginx

Файл xmlrpc.php — это реликт эпохи WordPress 2.x, созданный для удаленного управления сайтом по протоколу XML-RPC (например, для публикации записей из настольных клиентов или пингбэков). В современных версиях WordPress его полностью заменил быстрый и стандартизированный REST API (/wp-json/).

Хакеры и ботнеты используют XML-RPC по двум критическим причинам:

  • Амплификация перебора паролей: метод system.multicall позволяет в одном-единственном HTTP POST-запросе передать массив из 500 пар логин/пароль. Сервер тратит минуты на проверку, а в логах отображается всего одна строка!
  • DDoS-атаки через Pingback: злоумышленники заставляют тысячи уязвимых сайтов на WordPress одновременно пинговать сервер-жертву, превращая ваш VDS в участника ботнета.

Для 99% сайтов (интернет-магазины, блоги, корпоративные порталы) файл xmlrpc.php абсолютно бесполезен. Заблокируем к нему доступ на уровне виртуального хоста Nginx:

# /etc/nginx/sites-available/yoursite.conf

server {
    server_name example.com www.example.com;
    root /var/www/yoursite/public_html;

    # Полное отсечение xmlrpc.php с кодом 403 Forbidden
    location = /xmlrpc.php {
        deny all;
        access_log off;
        log_not_found off;
        return 403;
    }

    # Остальные директивы сайта...
}

Директивы access_log off; и log_not_found off; критически важны: они предотвращают забивание дискового пространства мусорными записями логов во время мощных атак.

Метод блокировки xmlrpc Нагрузка на CPU / RAM Скорость реакции Надежность защиты
Nginx location (Рекомендуется) Около 0% (отбой ядром Nginx) < 0.5 мс 100% (PHP даже не вызывается)
Файл .htaccess (Apache) Низкая (парсинг .htaccess) ~ 2–5 мс Высокая, но требует оверхед Apache
Плагин WordPress Высокая (полная загрузка PHP + MySQL) 50–200 мс Опасная (сервер легко положить наплывом)

3. Защита wp-login.php: лимиты limit_req, HTTP-авторизация и белый список

В отличие от XML-RPC, адрес входа в административную панель wp-login.php нельзя просто заблокировать для всех, ведь администраторам и редакторам сайта необходим доступ.

Применим комплексную трехуровневую стратегию:

Уровень 1. Рейт-лимитинг на уровне Nginx (ngx_http_limit_req_module)

Ограничим частоту обращений к странице авторизации с одного IP-адреса. В глобальный конфигурационный файл /etc/nginx/nginx.conf внутри блока http добавляем зону памяти для отслеживания IP:

# /etc/nginx/nginx.conf
http {
    # Выделяем 10 МБ памяти под IP-адреса (хватает на ~160 000 уникальных IP)
    # Ограничиваем скорость до 2 запросов в секунду на адрес
    limit_req_zone $binary_remote_addr zone=wplogin:10m rate=2r/s;
    limit_req_status 429;
    
    # ... остальные настройки
}

Теперь внутри блока server вашего виртуального хоста перехватываем обращения к wp-login.php:

# /etc/nginx/sites-available/yoursite.conf

location = /wp-login.php {
    # Разрешаем короткий всплеск до 4 запросов с немедленной блокировкой излишка
    limit_req zone=wplogin burst=4 nodelay;

    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Уровень 2. Дополнительная HTTP Basic Auth авторизация

Самый надежный способ полностью исключить ботнеты — закрыть вход на уровне веб-сервера окном базовой HTTP-аутентификации. Создадим защищенный файл с паролями с помощью утилиты htpasswd:

sudo apt install apache2-utils -y
sudo htpasswd -c /etc/nginx/.htpasswd admin_secure

Подключаем проверку в секции location = /wp-login.php:

location = /wp-login.php {
    auth_basic "SysKit Secure Area";
    auth_basic_user_file /etc/nginx/.htpasswd;

    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Теперь боты спотыкаются о стандартный запрос логина веб-сервера и получают код 401 Unauthorized без запуска интерпретатора PHP.

⚠️ Безопасность каталога загрузок uploads

Обязательно заблокируйте прямое исполнение любых PHP-скриптов в каталоге пользовательских файлов /wp-content/uploads/. Если злоумышленник загрузит бэкдор или веб-шелл через уязвимый плагин темы, Nginx просто откажется его выполнять:

location ~* ^/wp-content/uploads/.*\.php$ {
    deny all;
    access_log off;
    log_not_found off;
}

4. Блокировка атакующих через Fail2ban на уровне firewall (iptables/nftables)

Рейт-лимиты Nginx возвращают атакующим статус 429, но TCP-соединение все равно обрабатывается сетевым стеком веб-сервера. Чтобы полностью сбросить нагрузку, подключим Fail2ban, который будет автоматически блокировать IP-адреса нарушителей прямо в фаерволе на уровне ядра Linux.

Создадим фильтр регулярных выражений для отлова перебора паролей:

# /etc/fail2ban/filter.d/nginx-wp-login.conf
[Definition]
failregex = ^<HOST> -.* "(POST|GET) /wp-login.php.*" (200|401|403|429)
ignoreregex =

Теперь активируем джейл в файле локальной конфигурации /etc/fail2ban/jail.local:

# /etc/fail2ban/jail.local

[nginx-wp-login]
enabled  = true
port     = http,https
filter   = nginx-wp-login
logpath  = /var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime  = 86400
action   = iptables-multiport[name=WPLogin, port="http,https", protocol=tcp]

Правило работает предельно прозрачно: если с одного IP зафиксировано более 5 обращений к wp-login.php за 10 минут, адрес отправляется в бан на 24 часа. Пакеты с заблокированного IP будут отбрасываться фаерволом (DROP) еще до попадания в сокет Nginx.

# Перезапускаем Fail2ban и проверяем активность джейла
sudo systemctl restart fail2ban
sudo fail2ban-client status nginx-wp-login

5. Серверное микрокэширование Nginx FastCGI Cache взамен плагинов-монстров

Популярные плагины кэширования для WordPress (WP Super Cache, W3 Total Cache, WP Rocket) хороши для виртуального хостинга, где у пользователя нет доступа к конфигурации сервера. Но на собственном VDS использование PHP-плагинов для кэширования — это пустая трата ресурсов.

Встроенный модуль Nginx FastCGI Cache сохраняет сгенерированный HTML-код страниц в оперативную память или на сверхбыстрый NVMe-диск. При повторном запросе Nginx отдает готовую страницу напрямую, вообще не запуская PHP-FPM и не тревожа MySQL. Время генерации страницы сокращается со стандартных 400–900 мс до молниеносных 5–15 мс!

# /etc/nginx/conf.d/fastcgi_cache.conf

# Создаем зону кэша: 100 МБ под ключи, хранение до 60 минут, макс. объем 2 ГБ
fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m max_size=2g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header updating http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

В конфигурации виртуального хоста настраиваем обход кэша для авторизованных пользователей, административной панели и корзины:

# /etc/nginx/sites-available/yoursite.conf

# По умолчанию кэшируем
set $skip_cache 0;

# Не кэшируем POST-запросы
if ($request_method = POST) {
    set $skip_cache 1;
}

# Не кэшируем служебные разделы WordPress
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
    set $skip_cache 1;
}

# Не кэшируем авторизованных пользователей и покупателей WooCommerce
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in|woocommerce_items_in_cart") {
    set $skip_cache 1;
}

location ~ \.php$ {
    try_files $uri =404;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;
    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid 200 301 302 30m;

    # Добавляем заголовок для проверки статуса кэша (HIT / BYPASS / MISS)
    add_header X-FastCGI-Cache $upstream_cache_status;
}

Проверить работу кэша можно одной командой в терминале:

curl -I https://example.com/

При повторном запросе заголовок X-FastCGI-Cache вернет значение HIT. Это означает, что страница отдается напрямую из оперативной памяти со скоростью статического файла.

6. Оптимизация wp-config.php и перевод WP-Cron на системный crontab

Завершающий шаг наведения порядка — оптимизация системного файла wp-config.php.

Отключение псевдо-крона WordPress

По умолчанию планировщик задач WordPress (WP-Cron) устроен крайне неэффективно: при каждом посещении страницы случайным пользователем или роботом движок в фоновом режиме делает дополнительный HTTP-запрос к самому себе по адресу https://yoursite.com/wp-cron.php. При высокой нагрузке или параллельных запросах это приводит к дублированию фоновых задач и зависанию процессов PHP-FPM.

Отключаем встроенный крон в wp-config.php:

# wp-config.php

/* Отключаем встроенный WP-Cron при хитах пользователей */
define('DISABLE_WP_CRON', true);

/* Ограничиваем количество ревизий записей (чтобы не раздувать базу данных) */
define('WP_POST_REVISIONS', 5);

/* Увеличиваем интервал автосохранения до 3 минут */
define('AUTOSAVE_INTERVAL', 180);

/* Запрещаем редактирование файлов тем и плагинов через админку */
define('DISALLOW_FILE_EDIT', true);

/* Очищаем корзину автоматически раз в 14 дней */
define('EMPTY_TRASH_DAYS', 14);

Теперь настраиваем запуск системного планировщика Linux Cron от имени пользователя веб-сервера www-data раз в 15 минут:

# Открываем crontab пользователя www-data
sudo crontab -u www-data -e

# Добавляем строку выполнения планировщика:
*/15 * * * * php -q /var/www/yoursite/public_html/wp-cron.php > /dev/null 2>&1

Теперь отложенные публикации, отправка почты и проверка обновлений будут выполняться строго по расписанию в фоновом режиме, не задерживая генерацию страниц для пользователей.

7. Итоговый чек-лист стабильности и выбор надежного VDS

Применение приведенного комплекса мер гарантирует, что сайт на WordPress сможет легко выдерживать миллионные атаки ботнетов даже на бюджетном сервере.

Внедренная мера Точка применения Инженерный результат
Блокировка xmlrpc.php Nginx (return 403) Полное исключение скрытого перебора паролей и Pingback DDoS
Рейт-лимит wp-login.php Nginx limit_req Ограничение брутфорса до 2 запросов/сек, защита воркеров PHP
Сетевой бан Fail2ban iptables / nftables Автоматическая блокировка IP на 24 часа при 5 ошибках входа
Nginx FastCGI Cache RAM / NVMe кэш Ускорение TTFB до 10–20 мс, разгрузка MySQL и процессора на 90%
Системный Crontab Linux cron (www-data) Исключение зависаний страниц из-за фоновых задач WP-Cron

Однако никакой тюнинг Nginx не поможет, если виртуальный сервер работает на перегруженном хосте с медленными HDD или оверселлингом CPU. Для динамических CMS критически важны честная KVM-виртуализация, выделенные тактовые частоты ядер (от 3.0 ГГц) и быстрые накопители NVMe, гарантирующие высокий показатель IOPS при операциях с базой данных MySQL.

🚀 Нужен надежный и быстрый VDS для WordPress?

Разверните сервер у проверенных провайдеров с аппаратной KVM-виртуализацией, защитой от DDoS и скоростными NVMe-дисками. Это обеспечит бесперебойную работу ваших сайтов и баз данных 24/7.

Рекомендуемые VDS-провайдеры

Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с

Мощные процессоры AMD EPYC и NVMe

Timeweb Cloud VDS

Идеально подходит для высоконагруженных сайтов на WordPress и WooCommerce. Высокочастотные ядра до 3.7 ГГц, сверхбыстрые NVMe-накопители и готовые образы LEMP.

Конфигурация
от 1 vCPU / 2 GB RAM / 30 GB NVMe
Гарантированные ресурсы и каналы Tier III
🔷

Selectel Cloud VPS

Выделенные ресурсы без оверселлинга в надежных дата-центрах уровня Tier III. Идеальная изоляция для стабильной отдачи страниц и тяжелых очередей.

Конфигурация
от 1 vCPU / 2 GB RAM / 25 GB NVMe
Автобэкапы и простая панель
🟠

Beget VPS

Сбалансированные облачные серверы с ежедневным автоматическим резервным копированием баз данных, удобной панелью и круглосуточной поддержкой.

Конфигурация
от 1 vCPU / 2 GB RAM / 35 GB NVMe

Вопросы и ответы (FAQ)

Популярные вопросы вебмастеров и сисадминов по данной теме

Сломаются ли мобильное приложение WordPress или Jetpack при отключении xmlrpc.php?
Да, официальное мобильное приложение WordPress и некоторые старые модули Jetpack исторически используют протокол XML-RPC для синхронизации. Если вам критически необходима публикация через мобильное приложение, вместо полной блокировки настройте в Nginx белый список IP-адресов серверов Automattic/Jetpack либо ограничьте доступ базовой HTTP-аутентификацией.
Помогает ли плагин для смены адреса админки (например, переименование /wp-login.php)?
Смена адреса входа создает лишь иллюзию безопасности (Security through obscurity). Примитивные боты действительно перестанут находить страницу, однако продвинутые сканеры легко вычисляют новый URL по ссылкам в REST API, стилям или редиректам. Кроме того, плагины смены URL часто конфликтуют с кэшированием Nginx и сторонними модулями. Настройка Fail2ban и ограничение частоты запросов limit_req на уровне веб-сервера работает в разы надежнее и не нарушает логику движка.
Сколько оперативной памяти (RAM) требуется сайту на WordPress для стабильной работы на VDS?
Для корпоративного сайта, блога или визитки с настроенным FastCGI Cache достаточно минимального VDS с 1–2 ГБ RAM и 1 vCPU: кэш берет на себя до 95% запросов. Однако для интернет-магазинов на WooCommerce или проектов с десятками плагинов (Elementor, ACF, Yoast) рекомендуется конфигурация от 2 vCPU и 4 ГБ RAM, поскольку каждый динамический запрос в PHP-FPM требует 80–150 МБ оперативной памяти.

Читайте также в блоге SysKit

Смежные руководства и полезные технические статьи