Как закрыть XML-RPC от внешних запросов только для определённых IP в WordPress

Сценарий типичный: XML-RPC на сайте нужен, но не для всех. Например, его использует мобильное приложение, внешний сервис публикации или интеграция с мониторингом. При этом открытый xmlrpc.php часто становится лишней точкой входа для перебора методов и шумных запросов. Полностью отключать его не всегда удобно, поэтому практичнее ограничить доступ по IP.

Ниже — рабочая схема без выдуманных хуков и без «магии» на уровне плагинов. Сначала разберём, как понять, что именно у вас сейчас происходит, затем покажу два варианта: через сервер и через WordPress. Второй вариант полезен, если нет доступа к конфигу веб-сервера или нужно быстро проверить гипотезу.

Когда ограничение по IP действительно нужно

Закрывать XML-RPC по IP имеет смысл не «на всякий случай», а когда у вас есть понятный список доверенных источников. Это может быть один офисный адрес, IP сервиса деплоя, адрес VPN или подсеть, через которую работает интеграция. Если IP у сервиса плавающий, правило по одному адресу быстро начнёт мешать, и тогда лучше смотреть в сторону другого способа аутентификации или вообще отказаться от XML-RPC.

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

Диагностика: что проверить до изменений

Перед тем как что-то блокировать, проверьте три вещи: используется ли XML-RPC вообще, кто к нему обращается и не завязан ли на него критичный процесс. Иначе легко отрезать себе интеграцию и искать причину уже после инцидента.

Как понять, что endpoint живой

Откройте /xmlrpc.php в браузере или через curl. Нормальный ответ для GET-запроса обычно не означает, что метод можно вызвать, но сам файл доступен. Для более точной проверки используйте POST-запрос с тестовым методом.

curl -i https://example.com/xmlrpc.php

Если endpoint отвечает, это ещё не проблема. Проблема начинается, когда он доступен всем, хотя нужен только нескольким адресам.

Что смотреть в логах

Полезно проверить access log веб-сервера и, если есть, логи WAF или CDN. Ищите частые POST-запросы к /xmlrpc.php, особенно с разных IP и с ошибками авторизации. Это хороший признак того, что endpoint сканируют или используют для перебора.

  • много запросов к /xmlrpc.php без понятного источника;
  • повторяющиеся 401, 403 или 405 в ответах;
  • запросы из странных подсетей, не связанных с вашими сервисами;
  • ошибки в интеграциях после прошлых ограничений XML-RPC.

Варианты решения: сервер, WordPress или комбинированно

Если есть доступ к конфигу сервера, лучше ограничивать доступ там. Это дешевле по ресурсам: запросы отсекаются до загрузки WordPress. Если доступа к серверу нет, можно сделать проверку в mu-plugin или обычном плагине. Для production чаще выбирают серверный вариант, а WordPress-уровень оставляют как запасной.

ПодходПлюсыМинусы
Серверный allowlistРанний отказ, меньше нагрузки, проще для безопасностиНужен доступ к nginx/apache, сложнее при CDN и прокси
Проверка в WordPressМожно внедрить без правок сервераWordPress всё равно загружается, решение зависит от PHP
КомбинированныйМожно закрыть на сервере и оставить резервную проверку в кодеНужно следить, чтобы правила не конфликтовали

Пошаговое решение через сервер

Если сайт работает на nginx, самый прямой путь — разрешить xmlrpc.php только с нужных IP и всем остальным отдавать 403. Пример ниже рассчитан на отдельный location для файла.

location = /xmlrpc.php {
    allow 203.0.113.10;
    allow 198.51.100.0/24;
    deny all;

    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root/xmlrpc.php;
    fastcgi_pass php-fpm;
}

Если у вас Apache, логика та же: разрешаете доверенные адреса и запрещаете остальные. Для xmlrpc.php удобно использовать отдельное правило в .htaccess или конфиге виртуального хоста.

<Files "xmlrpc.php">
    Require ip 203.0.113.10
    Require ip 198.51.100.0/24
</Files>

Если сайт стоит за CDN или reverse proxy, проверьте, что сервер видит реальный IP клиента, а не адрес прокси. Иначе вы случайно разрешите только IP CDN, а не нужных пользователей. Для nginx это обычно решается настройкой real_ip_header и доверенных прокси, но конкретная конфигурация зависит от вашей схемы.

Проверка через WordPress-код, если сервер трогать нельзя

Когда доступа к конфигу нет, можно перехватить запрос до выполнения XML-RPC и вернуть 403 для всех, кроме доверенных IP. Для этого подходит фильтр xmlrpc_enabled. Он не отключает endpoint физически, но блокирует обработку XML-RPC на уровне WordPress.

<?php
add_filter('xmlrpc_enabled', function ($enabled) {
    $allowed_ips = array(
        '203.0.113.10',
        '198.51.100.0/24',
    );

    $remote_ip = $_SERVER['REMOTE_ADDR'] ?? '';

    if ($remote_ip === '') {
        return false;
    }

    foreach ($allowed_ips as $allowed) {
        if (wphide_ip_matches($remote_ip, $allowed)) {
            return $enabled;
        }
    }

    return false;
});

function wphide_ip_matches($ip, $allowed) {
    if (strpos($allowed, '/') === false) {
        return hash_equals($allowed, $ip);
    }

    list($subnet, $mask) = explode('/', $allowed, 2);
    $mask = (int) $mask;

    $ip_long = ip2long($ip);
    $subnet_long = ip2long($subnet);

    if ($ip_long === false || $subnet_long === false) {
        return false;
    }

    $mask_long = -1 << (32 - $mask);

    return (($ip_long & $mask_long) === ($subnet_long & $mask_long));
}

Этот вариант лучше размещать в mu-plugin, чтобы он не зависел от темы. Файл можно положить, например, в wp-content/mu-plugins/limit-xmlrpc-by-ip.php. Если mu-plugins у вас ещё нет, создайте каталог вручную.

Почему здесь не стоит использовать обычный плагин

Обычный плагин можно случайно отключить, удалить или забыть после обновления темы. Для ограничения доступа к XML-RPC это плохой сценарий: защита должна быть всегда активна. mu-plugin загружается автоматически и не требует активации в админке.

Как проверить, что ограничение сработало

Проверка должна быть не только «страница открывается», а именно по сценариям доступа. Сначала сделайте запрос с разрешённого IP, затем с запрещённого. Если у вас нет второго адреса под рукой, можно проверить через VPN или временно добавить тестовый IP в deny-список на сервере.

curl -i -X POST https://example.com/xmlrpc.php \
  -H 'Content-Type: text/xml' \
  --data '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName><params></params></methodCall>'

Что считать нормальным результатом:

  • с разрешённого IP endpoint отвечает так, как ожидает ваша интеграция;
  • с запрещённого IP возвращается 403 или обработка не доходит до XML-RPC;
  • в логах нет всплеска ошибок PHP после внедрения;
  • внешний сервис, который должен работать, продолжает авторизоваться.

Если вы ограничивали доступ через WordPress-фильтр, проверьте не только HTTP-код, но и то, что в логах не появляются фатальные ошибки из-за некорректной функции сравнения IP или пустого REMOTE_ADDR.

Частые ошибки и как их исправить

Забыли про прокси или CDN

Если сайт за Cloudflare, балансировщиком или nginx reverse proxy, REMOTE_ADDR может содержать адрес прокси. Тогда правило по IP не работает как задумано. Решение — сначала корректно настроить получение реального IP на уровне сервера, а потом уже строить allowlist.

Ограничили только один адрес, хотя сервис использует подсеть

У некоторых провайдеров IP меняется внутри диапазона. Если разрешить только один адрес, интеграция начнёт падать после очередной смены. Проверьте документацию сервиса: часто там есть список подсетей, которые можно разрешить целиком.

Положили код в тему

При смене темы защита исчезнет. Для таких задач используйте mu-plugin или отдельный мини-плагин, который не зависит от шаблона.

Смешали блокировку XML-RPC с отключением REST API

Это разные механизмы. Если у вас уже отключён REST API, а интеграция всё равно использует XML-RPC, проблемы будут не там, где вы их ищете. Сначала зафиксируйте, какой именно endpoint нужен конкретному сервису.

Практические советы по безопасности и производительности

Если XML-RPC нужен только для одного внешнего сервиса, держите allowlist коротким и документированным. Не оставляйте в правилах старые IP «на всякий случай» — это превращает ограничение в формальность. После смены подрядчика, VPN или хостинга пересматривайте список адресов.

Для сайтов с высокой нагрузкой серверный вариант предпочтительнее: он отсекает лишние запросы раньше и не тратит ресурсы PHP. Если у вас уже есть слой защиты вроде WAF, можно добавить правило и там, но не полагайтесь только на него: конфигурация WAF иногда меняется отдельно от сайта.

Если нужна более широкая чистка технических дублей и лишних точек входа, имеет смысл смотреть на инструменты уровня Clearfy Pro: они не заменяют точечную фильтрацию XML-RPC, но помогают убрать часть лишнего шума на сайте. Ставить такой инструмент стоит только если он реально закрывает ваши задачи, а не ради ещё одного слоя без понимания, что именно он делает.

В итоге рабочая схема простая: сначала выясняете, кто и зачем использует XML-RPC, потом ограничиваете доступ на сервере или через xmlrpc_enabled, затем проверяете реальные запросы и логи. Если после внедрения интеграция продолжает работать, а посторонние запросы получают отказ, задача решена правильно.

⭐⭐⭐⭐⭐