Как закрыть WP REST API от внешних запросов без поломки сайта

Ограничивать /wp-json/ «в лоб» опасно: REST API в WordPress используется не только внешними интеграциями, но и самим ядром, блок-редактором, некоторыми темами и плагинами. Поэтому задача обычно звучит не как «выключить API», а как «закрыть внешние запросы для гостей и ботов, но оставить работу админки и нужных интеграций».

Когда это действительно нужно

Чаще всего к этому приходят после одного из сценариев:

  • в логах много запросов к /wp-json/wp/v2/posts, /wp-json/wp/v2/users и похожим эндпоинтам;
  • боты собирают структуру сайта и перебирают пользователей;
  • на сайте есть публичный фронтенд, но REST API для него не нужен;
  • нужно уменьшить поверхность атаки без отключения важных функций WordPress.

Если у вас есть фронтенд на React/Vue, мобильное приложение, headless-сценарий или внешняя CRM-интеграция, закрывать API целиком нельзя. В таком случае ограничивают только часть маршрутов или доступ для неавторизованных пользователей.

Диагностика: что именно у вас ломается или светится наружу

Перед изменениями проверьте, кто и как использует API. Это помогает не гадать, а отключать только лишнее.

Проверьте, есть ли реальные обращения к REST API

Откройте логи веб-сервера или аналитики и посмотрите частоту запросов к адресам вида /wp-json/. Если у вас включён плагин безопасности или WAF, там тоже часто видно, какие маршруты дергают чаще всего.

Полезно отдельно проверить:

  • есть ли запросы от гостей без авторизации;
  • используются ли эндпоинты wp/v2 на фронтенде;
  • не завязаны ли на API формы, поиск, фильтры, редактор блоков;
  • не ломается ли предпросмотр записей в админке.

Быстрая проверка через браузер и curl

Откройте в браузере /wp-json/ и несколько типовых маршрутов. Для технической проверки удобнее использовать curl:

curl -I https://example.com/wp-json/
curl -I https://example.com/wp-json/wp/v2/posts
curl -I https://example.com/wp-json/wp/v2/users

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

Какой способ выбрать: плагин, код или сервер

СпособКогда подходитПлюсыМинусы
Плагин безопасностиНужно быстро закрыть доступ без разработкиМеньше риска ошибиться, есть UIЗависимость от стороннего кода, не всегда точечная настройка
Код в теме или mu-pluginНужно ограничить только часть маршрутовТочный контроль, без лишних зависимостейНужно тестировать совместимость
Правила на сервере/WAFНужно отрезать ботов ещё до WordPressНагрузка ниже, быстрее блокировкаСложнее не задеть легитимные запросы

Для большинства сайтов практичнее начинать с кода: так проще оставить доступ админке и закрыть только неавторизованные запросы к публичным маршрутам.

Пошаговое решение: ограничиваем REST API для гостей

Ниже вариант, который блокирует REST API для неавторизованных пользователей, но не мешает входу в админку и работе авторизованных сессий. Код лучше положить в mu-plugin или в отдельный мини-плагин, а не в functions.php активной темы.

<?php
/**
 * Plugin Name: Restrict REST API for guests
 */

add_filter( 'rest_authentication_errors', function( $result ) {
    if ( ! empty( $result ) ) {
        return $result;
    }

    if ( is_user_logged_in() ) {
        return $result;
    }

    // Разрешаем служебные запросы WordPress, если они понадобятся.
    $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';

    if ( strpos( $request_uri, '/wp-json/' ) === false ) {
        return $result;
    }

    return new WP_Error(
        'rest_forbidden',
        __( 'REST API доступен только авторизованным пользователям.', 'textdomain' ),
        array( 'status' => 403 )
    );
} );

Это грубая, но рабочая схема. Она подходит, если API не нужен на публичной части вообще. Если нужен частично, лучше ограничивать не весь API, а конкретные маршруты.

Точечная блокировка только части маршрутов

Например, можно закрыть список пользователей и оставить остальные эндпоинты доступными:

<?php
add_filter( 'rest_endpoints', function( $endpoints ) {
    if ( ! isset( $endpoints['/wp/v2/users'] ) ) {
        return $endpoints;
    }

    if ( current_user_can( 'list_users' ) ) {
        return $endpoints;
    }

    unset( $endpoints['/wp/v2/users'] );
    unset( $endpoints['/wp/v2/users/(?P<id>[\\d]+)'] );

    return $endpoints;
} );

Такой вариант полезен, если вам нужно убрать именно перечисление пользователей, но не ломать остальной REST API.

Что обязательно оставить доступным

Перед внедрением проверьте, не использует ли сайт:

  • блок-редактор Gutenberg в админке;
  • предпросмотр записей и страниц;
  • формы, которые отправляют данные через REST;
  • внешние интеграции с токенами или cookie-авторизацией;
  • плагины, которые подгружают данные через /wp-json/ на фронтенде.

Если есть сомнения, сначала ограничьте только чувствительные маршруты, а не весь API.

Проверка результата после внедрения

После включения ограничения проверьте не только код ответа, но и реальные сценарии на сайте.

Минимальный чек-лист

  • открывается ли админка без ошибок;
  • работает ли редактор записей и предпросмотр;
  • не сломались ли формы и поиск на фронтенде;
  • /wp-json/ для гостя возвращает ожидаемый 403 или другой заданный ответ;
  • авторизованный пользователь по-прежнему видит нужные маршруты;
  • в логах стало меньше лишних обращений к API.

Проверить ответ можно так:

curl -I https://example.com/wp-json/
curl -I https://example.com/wp-json/wp/v2/posts

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

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

Сломался редактор блоков

Причина обычно в слишком жёсткой блокировке всего REST API. Решение — не закрывать /wp-json/ полностью, а ограничить только внешние или чувствительные маршруты.

Перестали работать формы или фильтры

Некоторые плагины отправляют данные через REST, даже если это не видно в интерфейсе. Проверьте сетевые запросы в браузере и список активных плагинов. Если форма использует REST, добавьте исключение для её маршрута.

Блокировка сделана в теме, а потом тема обновилась

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

Ограничение сделано только на уровне robots.txt

Это не защита. robots.txt лишь просит поисковики не ходить по адресу, но не блокирует ботов и сканеры. Для реального ограничения нужен код, серверные правила или WAF.

Безопасность и производительность: что учесть заранее

Если цель — снизить нагрузку и шум в логах, лучше сочетать несколько мер:

  • закрыть ненужные маршруты REST API;
  • ограничить частоту запросов на уровне WAF или сервера, если это возможно;
  • не оставлять в публичном доступе лишние эндпоинты с данными пользователей;
  • проверить, не тянет ли тема или плагин данные через API без необходимости.

Для сайтов, где важна техническая чистота и SEO-гигиена, иногда удобнее сначала убрать дубли и лишние технические поверхности через инструменты вроде Clearfy Pro, а уже потом точечно ограничивать API. Но если у вас есть разработка под рукой, кодовый вариант обычно надёжнее и прозрачнее.

Главный критерий успеха здесь простой: после изменений сайт должен работать так же, как раньше, но внешние запросы к REST API должны быть либо закрыты, либо сведены к действительно нужным маршрутам.

⭐⭐⭐⭐⭐