REST API в WordPress нужен не только для Gutenberg и админки. Через него же часто дергают внешние сервисы, мобильные приложения, фронтенд на JavaScript и плагины. Проблема начинается там, где API остается полностью открытым, хотя сайт не использует публичные эндпоинты. Тогда лишние запросы светят структуру сайта, создают шум в логах и иногда помогают автоматизированному сбору данных.
Ниже — рабочий сценарий: ограничить REST API для гостей, но не сломать вход в админку, редактор и легитимные запросы авторизованных пользователей.
Когда это действительно нужно
Не стоит отключать REST API «на всякий случай». Сначала проверьте, есть ли у сайта зависимость от него. Если у вас обычный контентный сайт без фронтенд-редактора, без headless-части и без кастомных интеграций, ограничение часто оправдано. Если же сайт использует блоки, формы, поиск, личный кабинет или сторонний JS, нужно смотреть точечно.
Типичные симптомы
- в логах много запросов к
/wp-json/от ботов; - внешние скрипты обращаются к REST API без авторизации;
- плагин безопасности ругается на публичные эндпоинты;
- нужно скрыть служебные данные от незалогиненных посетителей;
- после аудита выяснилось, что сайт вообще не использует публичный REST API.
Диагностика: что именно открыто
Сначала не меняйте код. Проверьте, какие ответы отдает сайт на базовые запросы. Это поможет понять, можно ли ограничиться частичной блокировкой или нужен более аккуратный фильтр.
curl -I https://example.com/wp-json/Если сайт отвечает 200 OK, REST API доступен. Это нормально само по себе. Вопрос в том, какие именно маршруты вам нужны. Для проверки авторизованного доступа удобнее смотреть не только главную точку, но и конкретные эндпоинты, которые используют плагины или тема.
Еще один полезный тест — открыть /wp-json/wp/v2/posts в браузере в режиме инкогнито. Если там виден список записей, значит публичный доступ к контенту есть. Для новостного или корпоративного сайта это не всегда проблема, но для части проектов это лишняя поверхность атаки.
Подходы: плагин, код, серверная блокировка
Есть три рабочих варианта. Выбор зависит от того, насколько жестко вы хотите ограничить доступ и есть ли у вас контроль над сервером.
| Вариант | Что делает | Плюсы | Минусы |
|---|---|---|---|
| Плагин безопасности | Отключает или ограничивает REST API через интерфейс | Быстро, без кода | Меньше контроля, возможны конфликты |
| Код в теме или mu-plugin | Фильтрует ответы WordPress на уровне хуков | Точечно, прозрачно | Нужно тестировать вручную |
| .htaccess / nginx | Режет запросы до загрузки WordPress | Экономит ресурсы | Легко сломать легитимные запросы |
Если задача — убрать доступ только для гостей, самый безопасный путь обычно через код. Если нужно еще и снизить нагрузку, можно добавить серверное правило, но только после проверки, какие маршруты реально используются.
Пошаговое решение через код
Ниже пример, который запрещает REST API для неавторизованных пользователей, но оставляет доступ залогиненным. Такой вариант подходит для сайтов, где API нужен только в админке.
<?php
add_filter( 'rest_authentication_errors', function( $result ) {
if ( ! empty( $result ) ) {
return $result;
}
if ( is_user_logged_in() ) {
return $result;
}
return new WP_Error(
'rest_forbidden',
__( 'REST API доступен только авторизованным пользователям.', 'textdomain' ),
array( 'status' => 401 )
);
} );Код лучше размещать не в functions.php активной темы, а в небольшом mu-plugin. Тогда он не исчезнет после смены темы и не потеряется при обновлении.
<?php
/**
* Plugin Name: Restrict REST API for guests
*/
add_filter( 'rest_authentication_errors', function( $result ) {
if ( ! empty( $result ) ) {
return $result;
}
if ( is_user_logged_in() ) {
return $result;
}
return new WP_Error(
'rest_forbidden',
'REST API доступен только авторизованным пользователям.',
array( 'status' => 401 )
);
} );Если вам нужно оставить открытыми только отдельные маршруты, фильтр можно сделать мягче. Например, разрешить базовую информацию о сайте, но закрыть записи и таксономии.
<?php
add_filter( 'rest_request_before_callbacks', function( $response, $handler, $request ) {
if ( is_user_logged_in() ) {
return $response;
}
$route = $request->get_route();
if ( strpos( $route, '/wp/v2/posts' ) === 0 || strpos( $route, '/wp/v2/pages' ) === 0 ) {
return new WP_Error( 'rest_forbidden', 'Этот маршрут закрыт для гостей.', array( 'status' => 403 ) );
}
return $response;
}, 10, 3 );Этот вариант уже требует аккуратности: разные плагины могут использовать свои маршруты, и их тоже придется проверить отдельно.
Если нужен серверный запрет
На загруженных сайтах иногда имеет смысл отрезать часть запросов еще до WordPress. Но делать это стоит только если вы понимаете, какие URL должны остаться доступными.
Пример для Apache: блокировать публичный доступ к /wp-json/wp/v2/posts, но не трогать сам REST API целиком.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/wp-json/wp/v2/posts [NC]
RewriteRule .* - [F,L]
</IfModule>Для nginx логика будет похожей, но синтаксис зависит от конфигурации сервера. Если у вас нет доступа к конфигу, не пытайтесь имитировать это через WordPress — лучше ограничиться хуками.
Проверка результата после внедрения
После изменения не ограничивайтесь открытием главной страницы. Проверьте несколько сценариев.
- в инкогнито откройте
/wp-json/— должен быть отказ или ограниченный ответ; - залогиньтесь в админку и откройте редактор записи — Gutenberg должен работать;
- проверьте фронтенд, если там есть формы, поиск или динамические блоки;
- посмотрите консоль браузера на ошибки запросов к REST API;
- проверьте логи сервера на 401/403 и убедитесь, что это ожидаемые запросы.
Если после ограничения редактор перестал сохранять записи, значит вы закрыли слишком много. В этом случае сначала отключите серверное правило, потом упростите PHP-фильтр и оставьте только блокировку конкретных публичных маршрутов.
Частые ошибки и как их исправить
Полное отключение без проверки зависимостей
Самая частая ошибка — закрыть весь REST API и потом удивляться, что редактор, автосохранение или сторонний плагин перестали работать. Исправление простое: сначала выясните, какие маршруты использует сайт, и только потом режьте доступ.
Правило в теме вместо mu-plugin
Если код лежит в functions.php, он исчезнет при смене темы. Для технической настройки безопасности это плохое место. Используйте mu-plugin или отдельный мини-плагин.
Слишком грубая блокировка на сервере
Запретить /wp-json/ целиком — соблазнительно, но часто это ломает легитимные сценарии. Лучше блокировать конкретные маршруты, а не весь механизм.
Игнорирование кэш-плагинов и CDN
Иногда старые ответы продолжают отдаваться из кэша, и кажется, что ограничение не сработало. После изменений очистите кэш плагина, серверный кэш и CDN, если он есть.
Практические советы по безопасности и производительности
Если цель — не просто скрыть API, а уменьшить поверхность атаки, действуйте поэтапно. Сначала отключите публичные маршруты, которые точно не нужны. Потом проверьте, нет ли у вас плагинов, которые используют REST API только для служебных задач и могут быть заменены более простым механизмом.
Для аудита полезно держать под рукой список маршрутов и смотреть, кто их вызывает. В некоторых случаях достаточно закрыть только записи, страницы и таксономии, а не весь API. Это менее рискованно и обычно не мешает админке.
Если вам нужен более широкий набор технических настроек WordPress — от удаления дублей до чистки сайта и SEO-ограничений — у WPShop есть Clearfy Pro: https://wpshop.ru/plugins/clearfy. Но даже с плагином принцип остается тем же: сначала диагностика, потом точечное ограничение, затем проверка логов и фронтенда.
Если после внедрения у вас остались ошибки в консоли или неожиданные 401/403, не оставляйте их «на потом». Обычно это признак того, что какой-то плагин или кастомный скрипт использует REST API в фоне, и его маршрут нужно разрешить отдельно.