REST API часто оставляют открытым «по умолчанию», а потом удивляются лишним запросам, утечке служебных данных в публичные ответы и шуму в логах. Но закрывать его грубо через полную блокировку — плохая идея: Gutenberg, автосохранение, некоторые формы, мобильные приложения и внешние интеграции могут перестать работать.
Нормальная задача здесь не «выключить REST API целиком», а ограничить доступ для гостей и оставить рабочими нужные сценарии. Ниже — как это сделать аккуратно, как проверить результат и где чаще всего ошибаются.
Когда это вообще нужно
Сценарий обычно один из трёх:
- на сайте видны публичные ответы REST API, по которым можно собрать список записей, авторов или структуру таксономий;
- в логах много запросов к
/wp-json/от ботов и сканеров; - нужно снизить поверхность атаки, но не сломать админку и редактор.
Если сайт полностью статический по фронтенду и REST API не нужен внешним сервисам, ограничение для гостей часто оправдано. Если же у вас headless, мобильное приложение, формы или синхронизация с CRM, сначала проверьте, какие эндпоинты реально используются.
Диагностика: что именно открыто
Перед изменениями посмотрите, как отвечает сайт на базовые запросы. Это помогает понять, есть ли у вас уже защита на уровне плагина, темы или сервера.
curl -I https://example.com/wp-json/Если ответ 200 OK и в теле доступна структура API, значит публичный доступ не ограничен. Это не всегда проблема, но для многих сайтов лишняя экспозиция.
Полезно проверить и конкретный маршрут, например записи:
curl -I https://example.com/wp-json/wp/v2/postsЕсли вы видите данные без авторизации, значит API доступен гостям. Дальше важно решить, что именно закрывать: весь REST API или только часть маршрутов.
Рабочий подход: ограничить REST API только для неавторизованных
Самый безопасный вариант — блокировать запросы гостей через фильтр rest_authentication_errors. Он срабатывает до выдачи данных и позволяет вернуть корректный ответ 401 или 403.
Код для functions.php или mu-plugin
Лучше вынести это в маленький mu-plugin, чтобы правило не потерялось при смене темы. Если такой возможности нет, можно временно добавить в functions.php.
<?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'] ) : '';
// Пример: оставляем доступ к oEmbed и служебным маршрутам, если они нужны.
$allowed_prefixes = array(
'/wp-json/oembed/1.0/',
);
foreach ( $allowed_prefixes as $prefix ) {
if ( str_starts_with( $request_uri, $prefix ) ) {
return $result;
}
}
return new WP_Error(
'rest_forbidden',
__( 'REST API доступен только авторизованным пользователям.', 'textdomain' ),
array( 'status' => 401 )
);
} );Этот вариант жёсткий: он закрывает API для гостей почти полностью. Если у вас есть публичные эндпоинты, которые должны работать без логина, их лучше разрешать точечно, а не открывать всё подряд.
Если нужно закрыть только часть маршрутов
Иногда достаточно запретить, например, список записей и пользователей, но оставить рабочими отдельные endpoints для формы или фронтенд-виджета. Тогда проверяйте маршрут и возвращайте ошибку только для нежелательных путей.
<?php
add_filter( 'rest_authentication_errors', function( $result ) {
if ( ! empty( $result ) || is_user_logged_in() ) {
return $result;
}
$route = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
if ( str_contains( $route, '/wp-json/wp/v2/users' ) ) {
return new WP_Error( 'rest_forbidden', 'Маршрут закрыт для гостей.', array( 'status' => 403 ) );
}
if ( str_contains( $route, '/wp-json/wp/v2/posts' ) ) {
return new WP_Error( 'rest_forbidden', 'Маршрут закрыт для гостей.', array( 'status' => 403 ) );
}
return $result;
} );Такой способ грубее, чем полноценная настройка по ролям и маршрутам, но для небольших сайтов он часто практичнее: вы сразу видите, что именно блокируете.
Сравнение подходов
| Способ | Что даёт | Минус |
|---|---|---|
| Плагин безопасности | Быстрое включение без кода | Может скрывать логику и конфликтовать с другими правилами |
Код через rest_authentication_errors | Точный контроль над доступом | Нужно тестировать исключения вручную |
| Блокировка на уровне сервера | Снижает нагрузку раньше PHP | Легко сломать редактор и интеграции, если правила слишком широкие |
Если нужен быстрый и предсказуемый результат, кодовый вариант обычно удобнее. Плагин имеет смысл, когда на сайте уже есть централизованная политика безопасности и вы не хотите держать кастомный код в теме.
Проверка результата после внедрения
После сохранения изменений проверьте не только главную страницу, но и конкретные маршруты API.
- Откройте
/wp-json/в браузере без авторизации. - Проверьте
/wp-json/wp/v2/postsи/wp-json/wp/v2/users. - Зайдите в админку и откройте редактор записи.
- Создайте черновик и убедитесь, что автосохранение работает.
- Если есть формы, проверьте отправку и ответы AJAX-запросов.
Для быстрой проверки через консоль удобно смотреть статус-код:
curl -I https://example.com/wp-json/wp/v2/postsОжидаемый результат для гостя — 401 или 403 на закрытых маршрутах. Для авторизованного пользователя в админке запросы должны продолжать работать.
Частые ошибки и как их исправить
Сломали Gutenberg и автосохранение
Это происходит, когда блокируют весь REST API без исключений. Редактор использует API для сохранения контента и получения данных о записи. Решение — не резать всё подряд, а оставить авторизованных пользователей и нужные маршруты.
Закрыли API на уровне сервера слишком широко
Правила в Nginx или Apache часто пишут по шаблону и потом случайно блокируют не только /wp-json/, но и другие служебные запросы. Если вы не уверены в конфигурации, лучше сначала сделать ограничение на уровне WordPress, а уже потом переносить его в веб-сервер.
Не учли внешние интеграции
Формы, мобильные клиенты, фронтенд на React/Vue и некоторые плагины могут обращаться к REST API без явной подсказки в интерфейсе. Если после ограничения что-то перестало отвечать, ищите запросы к /wp-json/ в DevTools браузера и в логах сервера.
Проверяли только главную страницу
Это типичная ошибка. Главная может работать идеально, а редактор уже падает с ошибкой сохранения. Проверять нужно именно те сценарии, которые используют REST API: админка, редактор, формы, интеграции.
Практические советы по безопасности и производительности
Если цель — не просто закрыть доступ, а уменьшить шум и поверхность атаки, держите в голове несколько вещей:
- не используйте полную блокировку, пока не знаете, какие плагины завязаны на REST API;
- логируйте изменения и тестируйте после обновления плагинов;
- если маршрут нужен только для авторизованных, не оставляйте его открытым «на всякий случай»;
- не дублируйте одну и ту же защиту в плагине, теме и конфиге сервера одновременно без понимания порядка срабатывания;
- проверяйте, не кэширует ли CDN или reverse proxy ответы API отдельно от HTML.
Если вам нужен более широкий набор инструментов для чистки дублей, технической оптимизации и контроля SEO-деталей, можно посмотреть на Clearfy Pro: https://wpshop.ru/plugins/clearfy. Но даже с плагином важно понимать, какие именно маршруты вы закрываете и почему.
В итоге рабочая схема простая: сначала выясняете, какие REST-маршруты реально используются, потом ограничиваете доступ для гостей точечно, затем проверяете редактор, автосохранение и внешние интеграции. Если хотя бы один из этих шагов пропустить, «защита» быстро превращается в поломку сайта.