Открытый REST API в WordPress сам по себе не проблема. Проблема начинается, когда сайт начинает отдавать лишние данные гостям: списки записей, авторов, таксономий, пользовательские endpoint’ы плагинов. На небольших проектах это часто всплывает после аудита безопасности или при попытке убрать лишнюю индексацию технических URL.
Но закрывать /wp-json/wp/v2/ «в лоб» нельзя: редактор блоков, некоторые плагины и фронтенд-скрипты используют REST API для сохранения контента, автосохранения и подгрузки данных. Поэтому задача не в том, чтобы выключить API целиком, а в том, чтобы ограничить доступ для неавторизованных запросов и не сломать рабочие сценарии.
Когда ограничение REST API действительно нужно
Сценарий обычно один из трёх:
- сайт не использует публичные REST-endpoint’ы, но они доступны всем;
- в выдаче API видны данные, которые не должны быть доступны гостям;
- после включения SEO- или security-плагина появились ошибки в редакторе, потому что API закрыли слишком агрессивно.
Если у вас обычный корпоративный сайт, блог или лендинг, чаще всего достаточно оставить REST API для авторизованных пользователей и для конкретных публичных endpoint’ов, если они реально нужны. Полностью отключать его имеет смысл только после проверки зависимостей.
Диагностика: что именно сейчас открыто
Перед правкой кода проверьте, какие запросы реально доступны без авторизации. Это можно сделать из браузера или через curl.
curl -I https://example.com/wp-json/wp/v2/postsЕсли в ответе вы видите 200 OK, значит endpoint доступен гостям. Для некоторых сайтов это нормально, но если вы хотите закрыть API, такой ответ как раз подтверждает проблему.
Полезно отдельно проверить:
/wp-json/— общий индекс API;/wp-json/wp/v2/posts— записи;/wp-json/wp/v2/users— пользователи;- endpoint’ы плагинов, если они есть.
Если после ограничения API в админке перестаёт работать редактор, автосохранение или медиа-загрузка, значит правило задело и авторизованные запросы тоже. В этом случае нужно возвращать доступ для залогиненных пользователей и проверять, не блокируется ли admin-ajax.php или nonce-проверка в JavaScript.
Рабочая схема: закрыть REST API для гостей и оставить его для авторизованных
Самый безопасный вариант — не трогать всё подряд, а ограничить только неавторизованные запросы. Для этого подходит фильтр rest_authentication_errors. Он срабатывает до выполнения endpoint’а и позволяет вернуть ошибку для гостей.
Вариант на коде: блокировать гостей, кроме служебных запросов
Добавьте код в functions.php дочерней темы или в небольшой mu-plugin. Второй вариант надёжнее, потому что не зависит от темы.
<?php
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/') === false) {
return $result;
}
return new WP_Error(
'rest_forbidden',
__('REST API доступен только авторизованным пользователям.', 'textdomain'),
array('status' => 401)
);
});Этот вариант жёсткий: он закрывает REST API для гостей целиком. Подходит не всем. Если на сайте есть публичные endpoint’ы, лучше ограничить только конкретные маршруты.
Вариант точечно: закрыть только wp/v2 для гостей
Если вам нужно оставить API для плагина или собственного endpoint’а, но скрыть стандартные данные WordPress, используйте проверку маршрута.
<?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 (strpos($route, '/wp-json/wp/v2/') !== false) {
return new WP_Error(
'rest_forbidden',
__('Стандартные REST endpoint’ы закрыты для гостей.', 'textdomain'),
array('status' => 401)
);
}
return $result;
});Такой подход не идеален с точки зрения архитектуры, потому что опирается на URI, но для практической задачи он понятен и предсказуем. Если нужен более строгий контроль, можно фильтровать уже на уровне маршрутов через rest_endpoints, но это сложнее и требует аккуратной настройки.
Сравнение подходов
| Подход | Что делает | Плюсы | Минусы |
|---|---|---|---|
| Плагин безопасности | Ограничивает REST API настройками | Быстро, без кода | Может закрыть лишнее, зависит от интерфейса плагина |
Код через rest_authentication_errors | Блокирует гостей на уровне WordPress | Контроль и прозрачность | Нужно тестировать зависимости |
| Отключение API целиком | Ломает доступ ко всем endpoint’ам | Максимально жёстко | Высокий риск поломки редактора и плагинов |
Если задача — именно безопасность и уменьшение поверхности атаки, кодовый вариант обычно лучше. Если нужен быстрый результат без разработки, можно использовать security-плагин, но после этого обязательно проверить редактор, формы и интеграции.
Пошаговая настройка без сюрпризов
- Сделайте резервную копию файлов и базы.
- Проверьте, какие REST endpoint’ы используются на сайте.
- Добавьте ограничение сначала на staging-копии.
- Откройте главную страницу, запись, страницу редактирования и форму обратной связи.
- Проверьте, не появились ли ошибки в консоли браузера.
- Только после этого переносите правку на продакшен.
Если у вас есть собственные endpoint’ы, добавьте для них исключения. Например, публичный endpoint для мобильного приложения или внешнего сервиса не должен внезапно начать отдавать 401.
Как проверить, что решение сработало
Проверка должна быть не только по коду ответа, но и по поведению сайта.
- Откройте
/wp-json/wp/v2/postsв режиме инкогнито: должен вернуться401или ваш текст ошибки. - Авторизуйтесь в админке и повторите запрос: endpoint должен открываться, если вы его не закрывали специально для всех.
- Проверьте редактор блоков: создание и сохранение записи должны работать.
- Посмотрите консоль браузера на странице редактирования: не должно быть ошибок
REST API request failed. - Если есть формы, интеграции или SPA-фронтенд, протестируйте их отдельно.
Для быстрой проверки можно использовать ещё и wp_remote_get() из временного сниппета или тестового плагина, но на практике достаточно браузера и curl.
Частые ошибки и как их исправить
Закрыли API целиком и сломали редактор
Причина почти всегда одна: фильтр возвращает ошибку для всех запросов, включая авторизованные. Исправление простое — сначала проверяйте is_user_logged_in(), а уже потом блокируйте гостей.
Сломались публичные endpoint’ы плагина
Некоторые плагины используют REST API для фронтенда. Если вы закрыли только /wp-json/wp/v2/, а плагин работает через собственный маршрут, проблема может быть в другом месте. Проверьте, какой именно URL вызывает ошибку, и добавьте исключение для него.
Ошибка есть в браузере, но не на сервере
Иногда REST API блокирует не WordPress, а WAF, CDN или хостинг-фильтр. Тогда в логике WordPress всё выглядит правильно, но запросы всё равно режутся. Смотрите заголовки ответа и серверные логи.
Слишком общий текст ошибки
Если возвращать одинаковый ответ для всех случаев, потом трудно понять, что именно сломалось. Для отладки полезно временно различать ошибки по маршрутам, а после проверки оставить один нейтральный текст.
Производительность и безопасность: что ещё стоит учесть
Ограничение REST API не заменяет базовую защиту сайта. Если цель — уменьшить лишние точки доступа, проверьте ещё и XML-RPC, индексацию служебных страниц и доступ к авторским архивам. Но не смешивайте всё в одно правило: чем грубее блокировка, тем выше риск побочных эффектов.
Если вы часто вносите такие правки, удобнее хранить их в отдельном mu-plugin. Тогда изменение не потеряется при смене темы и не будет зависеть от обновлений шаблона.
<?php
/**
* Plugin Name: REST API Access Control
*/
add_filter('rest_authentication_errors', function ($result) {
if (!empty($result) || is_user_logged_in()) {
return $result;
}
$uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : '';
if (strpos($uri, '/wp-json/wp/v2/') !== false) {
return new WP_Error('rest_forbidden', 'REST API закрыт для гостей.', array('status' => 401));
}
return $result;
});После внедрения не забудьте удалить временные тестовые правила и проверить, не осталось ли в кэше старых ответов. Если сайт использует page cache или CDN, очистка кэша обязательна: иначе вы можете увидеть старую версию ответа и решить, что правило не сработало.