Как отключить JSON REST API для гостей в WordPress и не сломать редактор

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

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

Когда это вообще имеет смысл

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

Такой подход уместен, если:

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

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

Перед изменениями проверьте, что реально отдает сайт. Откройте в браузере или через 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

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

Что нельзя ломать

Полностью рубить REST API на уровне rest_api_init или через агрессивные правила в .htaccess — плохая идея. Gutenberg и часть админки используют REST-запросы. Если отрезать все подряд, получите ошибки сохранения записей, пустые панели или неработающие плагины.

Пошаговое решение через фильтр rest_authentication_errors

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

Добавьте код в functions.php дочерней темы или в небольшой must-use плагин:

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

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

    // Не блокируем запросы, которые WordPress сам считает служебными.
    if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
        return new WP_Error(
            'rest_forbidden',
            __( 'REST API доступен только авторизованным пользователям.', 'textdomain' ),
            array( 'status' => 401 )
        );
    }

    return $result;
} );

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

Более безопасный вариант: разрешить только нужные маршруты

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

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

    if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
        return $result;
    }

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

    // Разрешаем служебные и публичные маршруты, если они нужны сайту.
    $allowed_patterns = array(
        '#^/wp-json/$#',
        '#^/wp-json/oembed/1\.0/#',
        '#^/wp-json/wp/v2/posts#',
    );

    foreach ( $allowed_patterns as $pattern ) {
        if ( preg_match( $pattern, $uri ) ) {
            return $result;
        }
    }

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

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

Сравнение подходов: плагин, код, сервер

ПодходЧто делаетПлюсыМинусы
Код через rest_authentication_errorsБлокирует REST для гостей на уровне WordPressТочно контролируется, легко откатитьНужно тестировать исключения
Плагин безопасностиМожет скрывать или ограничивать APIУдобно без кодаЛогика зависит от плагина, возможны конфликты
Правила сервераРежет запросы до загрузки WordPressСнимает нагрузкуЛегко сломать редактор и служебные запросы

Если нужен быстрый и управляемый результат, код обычно надежнее. Если задача шире — например, убрать дубли, закрыть технические страницы и почистить сайт комплексно, иногда удобнее использовать инструменты вроде Clearfy Pro, но только если вам реально нужен именно набор SEO/технических настроек, а не один частный фикс.

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

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

  • Откройте /wp-json/ в режиме инкогнито — должен быть отказ или ограниченный ответ.
  • Зайдите в админку и откройте запись в Gutenberg — сохранение должно работать.
  • Проверьте медиа, категории и автосохранение в редакторе.
  • Если есть фронтенд-форма или виджет, который читает данные через REST, убедитесь, что он не сломался.

Для быстрой проверки можно снова использовать curl:

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

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

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

Блокируют REST API через init или template_redirect

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

Не проверяют авторизацию

Если не добавить проверку is_user_logged_in(), можно случайно закрыть доступ даже для администраторов в некоторых сценариях. Это особенно заметно при работе с редактором и AJAX-логикой в админке.

Режут все маршруты без исключений

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

Проверяют только главную страницу

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

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

Ограничение REST API само по себе не заменяет базовую защиту. Если цель — уменьшить поверхность атаки, проверьте еще несколько вещей:

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

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

В итоге рабочая схема простая: сначала выясняете, какие REST-маршруты реально нужны сайту, затем ограничиваете доступ гостям через rest_authentication_errors, после этого проверяете редактор, админку и публичные интеграции. Если что-то ломается, не откатывайте всё сразу — сначала расширьте белый список до нужных маршрутов.

Автоматическое удаление неактивных пользователей WordPress
14.04.2026
Как настроить производительность WordPress на уровне кода
03.12.2025
Оптимизация базы данных WordPress: практические советы и примеры кода
09.11.2025
Как создать динамические формы обратной связи в WordPress с примерами кода
05.02.2026
Создание собственной таблицы базы данных в WordPress с примерами кода
25.12.2025