Если на сайте в логах или в аудитах всплывают публичные запросы к /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/postscurl -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, после этого проверяете редактор, админку и публичные интеграции. Если что-то ломается, не откатывайте всё сразу — сначала расширьте белый список до нужных маршрутов.