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

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

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

Когда проблема реально есть

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

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

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

Что проверить до изменений

  • Откройте /wp-json/ в браузере и посмотрите, отдаёт ли сайт индекс маршрутов.
  • Проверьте /wp-json/wp/v2/users и похожие публичные endpoints.
  • Посмотрите логи веб-сервера: есть ли всплеск запросов к /wp-json/.
  • Убедитесь, что редактор Gutenberg и админка работают в обычном режиме.

Как закрыть публичный REST API через код

Самый предсказуемый способ — добавить фильтр rest_authentication_errors. Он позволяет вернуть ошибку для гостей, но не мешает авторизованным пользователям и внутренним вызовам, если настроить его аккуратно.

Добавлять код лучше в мини-плагин или в functions.php дочерней темы. Для продакшена мини-плагин надёжнее: он не зависит от темы.

<?php
/**
 * Plugin Name: Restrict Public REST API
 */

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

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

    // Разрешаем только запросы, которые явно нужны гостям.
    $uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';

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

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

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

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

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

<?php
add_filter( 'rest_endpoints', function( $endpoints ) {
    if ( is_user_logged_in() ) {
        return $endpoints;
    }

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

    return $endpoints;
} );

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

Если нужен серверный уровень: что можно сделать в Nginx или Apache

Серверные правила полезны, когда нужно отрезать шум ещё до загрузки WordPress. Но здесь важно не переусердствовать: если вы заблокируете /wp-json/ без оглядки, можете сломать фронтенд, редактор или внешние интеграции.

ПодходПлюсМинус
PHP-фильтрТочечный контроль, проще тестироватьWordPress всё равно загружается
Nginx/Apache правилоМеньше нагрузки на серверЛегко заблокировать лишнее
Плагин безопасностиБыстро включить без кодаЗависимость от сторонней логики

Если вы уверены, что публичный REST API не нужен вообще, можно вернуть 403 на уровне веб-сервера. Для Nginx это выглядит так:

location ~* ^/wp-json/ {
    return 403;
}

Для Apache можно использовать правило в .htaccess, но только если вы понимаете последствия и у вас нет сценариев, завязанных на REST API:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/wp-json/ [NC]
RewriteRule .* - [F,L]
</IfModule>

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

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

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

  • Откройте /wp-json/ в режиме инкогнито: должен быть 403 или минимальный ответ, если вы оставили публичный доступ.
  • Проверьте /wp-json/wp/v2/users: он не должен отдавать список пользователей гостю.
  • Зайдите в админку и откройте редактор записи: сохранение и автосохранение должны работать.
  • Если есть формы, AJAX или внешние интеграции, протестируйте их отдельно.
  • Посмотрите error log и access log: не должно быть лавины 403 от нужных внутренних запросов.

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

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

Закрыли весь REST API и сломали редактор

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

Использовали слишком широкий серверный фильтр

Правило вида location /wp-json/ { return 403; } без проверки сценариев может задеть легитимные запросы. Перед применением убедитесь, что у вас нет headless-фронтенда, мобильного приложения или интеграции с внешним сервисом.

Скрыли только часть маршрутов, но оставили утечку пользователей

Если цель — убрать перечисление пользователей, проверьте не только /wp/v2/users, но и связанные endpoints, которые могут светить данные через метаданные или авторские архивы. Иногда проблема решается не блокировкой API, а ограничением прав на просмотр профилей и отключением лишних полей в ответе.

Не проверили кэш

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

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

Если вы ограничиваете REST API ради безопасности, не останавливайтесь только на этом. Проверьте ещё несколько вещей:

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

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

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

⭐⭐⭐⭐⭐