Как закрыть REST API для гостей в WordPress без поломки сайта

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

Ниже — рабочий подход: сначала понять, что именно у вас сейчас открыто, затем закрыть лишнее точечно, проверить результат и не наступить на типовые ошибки.

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

Не каждый сайт нуждается в жестком закрытии API. Но если у вас есть хотя бы один из этих сценариев, проверка точно не будет лишней:

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

Важно не путать защиту REST API с закрытием всего сайта. Если вы просто запретите все запросы, редактор блоков, AJAX-интеграции и некоторые плагины начнут вести себя непредсказуемо. Поэтому сначала нужна диагностика.

Диагностика: что именно открыто сейчас

Начните с проверки базового ответа API. Откройте в браузере или через curl адрес /wp-json/. Если сайт публичный, вы увидите список маршрутов и namespace'ов. Это нормально для стандартного WordPress, но именно здесь видно, какие плагины добавили свои endpoints.

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

Если вы хотите понять, как API отвечает для гостей и для авторизованных пользователей, проверьте оба сценария отдельно. Для гостя достаточно обычного запроса. Для авторизованного пользователя тестируйте через браузер с активной сессией или через инструмент, который умеет передавать cookies.

Что смотреть в ответе:

  • код ответа: 200, 401, 403 или 404;
  • есть ли в JSON маршруты, которые не должны быть публичными;
  • не отдает ли endpoint персональные или технические данные;
  • не ломается ли редактор блоков после ограничений.

Какие маршруты обычно нельзя закрывать полностью

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

Рабочие способы ограничить REST API

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

СпособПлюсыМинусыКогда выбирать
Плагин безопасностиБыстро, без кодаМожет закрыть лишнее или конфликтоватьЕсли нужен быстрый старт и есть понятные настройки
Код через rest_authentication_errorsТочно контролируете логикуНужно тестироватьЕсли нужен точечный доступ для гостей
Серверные правилаСнимают нагрузку до PHPСложнее поддерживать, легко ошибитьсяЕсли есть опыт с nginx/apache и понятный набор маршрутов

Вариант 1. Ограничить REST API для гостей через код

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

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

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

    $request_uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : '';

    // Разрешаем только нужные публичные маршруты.
    $allowed_prefixes = array(
        '/wp-json/wp/v2/posts',
        '/wp-json/wp/v2/pages',
        '/wp-json/oembed/1.0',
    );

    foreach ($allowed_prefixes as $prefix) {
        if (strpos($request_uri, $prefix) === 0) {
            return $result;
        }
    }

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

Этот вариант подходит, если вам нужно закрыть API для гостей, но оставить отдельные публичные маршруты. Важно: проверяйте не только главную страницу API, но и конкретные endpoints, которые используют плагины.

Вариант 2. Закрыть только служебные маршруты

Иногда не нужно ограничивать весь API. Достаточно убрать доступ к конкретным namespace'ам, которые добавляет плагин или кастомная тема. Тогда можно фильтровать запросы по префиксу и возвращать 403 только для нежелательных путей.

<?php
add_filter('rest_pre_dispatch', function ($result, $server, $request) {
    if (is_user_logged_in()) {
        return $result;
    }

    $route = $request->get_route();

    if (strpos($route, '/my-plugin/v1/') === 0) {
        return new WP_Error(
            'rest_forbidden_route',
            __('Этот endpoint недоступен для гостей.', 'textdomain'),
            array('status' => 403)
        );
    }

    return $result;
}, 10, 3);

Такой подход удобен, когда вы знаете, какой именно endpoint надо спрятать. Он не ломает весь API и позволяет сохранить рабочие интеграции.

Вариант 3. Скрыть REST API на уровне сервера

Если задача — снизить нагрузку от ботов, можно отрезать часть запросов на уровне nginx или Apache. Но это уже не универсальное решение: серверные правила легко задеть редактор, мобильное приложение или интеграцию, если не учесть исключения. Поэтому такой способ имеет смысл только при четком понимании, какие маршруты реально нужны.

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

Пошаговая настройка без лишнего риска

  1. Сделайте резервную копию файлов и базы.
  2. Проверьте, какие плагины используют REST API.
  3. Решите, что именно нужно оставить публичным: записи, страницы, oEmbed, формы, поиск.
  4. Добавьте ограничение в mu-plugin или в дочернюю тему, а не в основной плагин, который может обновляться.
  5. Протестируйте сайт в режиме гостя и под администратором.
  6. Проверьте редактор блоков, формы, поиск, фильтры и любые виджеты, которые тянут данные через API.

Если вы не хотите править тему, удобнее сделать маленький mu-plugin. Он не зависит от активации темы и не потеряется после обновления.

<?php
/**
 * Plugin Name: REST API Access Control
 */

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

    $uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : '';

    if (strpos($uri, '/wp-json/wp/v2/posts') === 0) {
        return $result;
    }

    return new WP_Error('rest_forbidden', 'REST API закрыт для гостей.', array('status' => 401));
});

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

Проверка должна быть не формальной, а по сценариям. Сначала откройте API в приватном окне браузера. Затем проверьте тот же endpoint под администратором. Если вы закрывали только часть маршрутов, убедитесь, что разрешенные адреса продолжают отвечать нормально.

    ⭐⭐⭐⭐⭐