REST API в WordPress часто оставляют открытым по умолчанию, а потом удивляются лишним запросам к /wp-json/, утечке служебных данных или шуму в логах. Полностью отключать API обычно не нужно: это ломает редактор блоков, мобильные приложения, интеграции и часть плагинов. Но ограничить публичный доступ к отдельным маршрутам или скрыть служебные данные — нормальная техническая задача.
Ниже — рабочий сценарий: сначала понять, что именно у вас торчит наружу, затем закрыть только лишнее, не ломая админку и фронтенд.
Когда REST API действительно стоит ограничить
Не каждый сайт нуждается в полном открытии всех маршрутов. Проблема обычно проявляется в трех случаях: сайт получает много бессмысленных запросов к /wp-json/, в ответах видны данные, которые не должны быть доступны анонимно, или внешний сервис ходит в API без авторизации и получает больше, чем должен.
Типичный пример — публичный сайт, где нужен только стандартный REST для редактора и пары интеграций, но при этом доступны пользовательские маршруты плагина, метаданные записей или кастомные эндпоинты без проверки прав. В такой ситуации лучше не «рубить» API целиком, а сузить доступ.
Что не стоит делать
Не отключайте REST API через случайный сниппет из интернета, если не понимаете, какие плагины и темы его используют. Частая ошибка — ломается Gutenberg, превью записей, формы, поиск по сайту, интеграции с внешними сервисами и даже часть админки. Если на сайте есть headless-функции, мобильное приложение или синхронизация с CRM, полный запрет почти наверняка навредит.
Диагностика: что именно открыто
Сначала посмотрите, какие ответы отдает API без авторизации. Самый простой способ — открыть в браузере /wp-json/ и проверить список маршрутов. Для более точной диагностики используйте curl и смотрите коды ответа.
curl -I https://example.com/wp-json/Если нужен список маршрутов, можно запросить JSON и посмотреть, какие namespace присутствуют. На живом сайте это помогает быстро понять, откуда идут лишние эндпоинты: ядро, тема или конкретный плагин.
curl -s https://example.com/wp-json/ | head -c 1200Обратите внимание на такие признаки:
- маршруты плагинов, которые не нужны на публичной части сайта;
- эндпоинты с данными пользователей, комментариев, медиа или таксономий без явной необходимости;
- ошибки 200 на запросы, которые должны требовать авторизацию;
- большое количество обращений к
/wp-json/в логах веб-сервера.
Как ограничить REST API без поломки сайта
Самый безопасный подход — не отключать REST API глобально, а фильтровать доступ к конкретным маршрутам. Для этого в WordPress есть фильтр rest_authentication_errors. Он позволяет вернуть ошибку для анонимных запросов, если маршрут не должен быть публичным.
Ниже пример: разрешаем стандартные маршруты ядра и блокируем все остальное для неавторизованных пользователей. Это грубый вариант, его стоит применять только после проверки зависимостей. На практике чаще закрывают только конкретные namespace или маршруты.
<?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'] ) : '';
if ( strpos( $request_uri, '/wp-json/wp/v2/' ) !== false ) {
return $result;
}
return new WP_Error(
'rest_forbidden',
__( 'REST API доступен только авторизованным пользователям.' ),
array( 'status' => 403 )
);
} );Этот вариант демонстрационный. Он не идеален, потому что опирается на URI, а не на сам объект запроса. Для более точечной блокировки лучше использовать rest_pre_dispatch или регистрировать свои маршруты с проверкой permission_callback.
Если вы пишете свой REST-маршрут
Для собственных эндпоинтов не оставляйте permission_callback пустым. Это одна из самых частых ошибок. WordPress требует явной проверки прав, и это не формальность.
<?php
add_action( 'rest_api_init', function() {
register_rest_route( 'myplugin/v1', '/stats', array(
'methods' => 'GET',
'callback' => 'myplugin_get_stats',
'permission_callback' => function() {
return current_user_can( 'manage_options' );
},
) );
} );
function myplugin_get_stats( WP_REST_Request $request ) {
return rest_ensure_response( array(
'ok' => true,
) );
}Если маршрут нужен только для авторизованных пользователей, проверка прав должна быть в самом маршруте, а не в шаблоне темы или на фронтенде. Иначе анонимный запрос все равно дойдет до PHP.
Сравнение подходов: плагин, код или сервер
Иногда удобнее закрыть доступ на уровне плагина безопасности, но это не всегда точнее, чем код. Если задача узкая, код обычно предсказуемее. Если нужен быстрый административный контроль без разработки, можно использовать плагин, но тогда нужно понимать его логику и ограничения.
| Подход | Когда подходит | Минус |
|---|---|---|
Код в functions.php или mu-plugin | Нужно закрыть конкретные маршруты или поведение | Требует проверки после обновлений |
| Плагин безопасности | Нужны готовые настройки без разработки | Может закрыть лишнее или конфликтовать с интеграциями |
| Правила на сервере | Нужно снизить шум от массовых запросов | Сложнее не сломать легитимные запросы |
Если у вас уже стоит комплексный плагин для технической чистки сайта, например Clearfy Pro, часть задач по скрытию служебных данных может быть закрыта настройками без ручного кода. Но перед включением любой опции все равно проверьте, не использует ли сайт REST API для фронтенда или внешних сервисов.
Пошаговое решение для типового сайта
Для большинства проектов разумно идти так: сначала собрать список маршрутов, затем закрыть только лишние, потом проверить фронтенд и админку. Не начинайте с тотального запрета.
- Откройте
/wp-json/и выпишите namespace, которые не относятся к ядру WordPress. - Проверьте, какие плагины используют REST API на фронтенде.
- Добавьте ограничение только для конкретных маршрутов или namespace.
- Протестируйте вход, редактор блоков, предпросмотр записей и формы.
- Посмотрите логи сервера и убедитесь, что лишние анонимные запросы получили 401 или 403, а не 200.
Пример точечной блокировки namespace
Если нужно закрыть только один namespace, удобнее проверять запрос до выполнения маршрута. Ниже пример для блокировки пользовательского namespace myplugin/v1 для анонимных пользователей.
<?php
add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) {
$route = $request->get_route();
if ( 0 === strpos( $route, '/myplugin/v1/' ) && ! is_user_logged_in() ) {
return new WP_Error(
'rest_forbidden',
__( 'Доступ к этому маршруту закрыт.' ),
array( 'status' => 403 )
);
}
return $result;
}, 10, 3 );Такой вариант лучше, чем блокировать весь /wp-json/. Он не мешает ядру и дает понятную точку контроля.
Как проверить, что решение сработало
Проверка должна быть не только визуальной. Убедитесь, что анонимный запрос получает нужный код ответа, а авторизованный пользователь — рабочий ответ. Это особенно важно, если вы ограничивали маршруты для редактора или внешней интеграции.
- Откройте закрытый маршрут в браузере в режиме инкогнито.
- Проверьте ответ через
curl -iи убедитесь, что это403или401, а не200. - Войдите в админку и повторите запрос — маршрут должен работать, если это предусмотрено логикой.
- Проверьте редактор записей, предпросмотр и AJAX-формы на фронтенде.
- Посмотрите error log и access log: не должно быть всплеска PHP-ошибок после изменения.
curl -i https://example.com/wp-json/myplugin/v1/statsЕсли маршрут закрыт правильно, вы увидите либо JSON с ошибкой WordPress, либо HTTP-статус 403. Если вместо этого приходит 200, значит ограничение не сработало или маршрут обходится другим способом.
Частые ошибки и как их исправить
Ошибка 1: полностью отключили REST API. В результате перестал работать редактор блоков или интеграция плагина. Решение — откатить глобальный запрет и закрывать только конкретные маршруты.
Ошибка 2: забыли про permission_callback в собственном маршруте. В таком случае маршрут может быть доступен шире, чем вы планировали. Исправление простое: добавьте проверку прав через current_user_can().
Ошибка 3: блокировка по URI ломает часть запросов. Если фильтр завязан на строку запроса, он может сработать не так, как ожидается, при изменении структуры маршрута. Лучше проверять объект WP_REST_Request и route.
Ошибка 4: не протестировали кэш. Иногда CDN или page cache отдает старый ответ, и кажется, что правило не работает. После изменений очистите кэш на всех уровнях: плагин, сервер, CDN.
Ошибка 5: закрыли API, но забыли про утечки в других местах. REST — только один канал. Если нужно скрыть служебные данные, проверьте также XML-RPC, публичные авторские архивы, sitemap и JSON-ответы других плагинов.
Безопасность и производительность
Ограничение REST API не должно превращаться в хрупкий набор костылей. Если у вас много кастомных маршрутов, держите их в отдельном mu-plugin или в плагине проекта, а не в теме. Так проще сопровождать и не потерять логику при смене шаблона.
Для производительности полезно не только закрыть лишнее, но и убрать шумные маршруты, которые дергаются без пользы. Это снижает нагрузку на PHP и уменьшает количество мусорных запросов в логах. Если API нужен только для части функциональности, не делайте его публичным по умолчанию.
Если вы используете плагины, которые активно работают через REST, сначала проверьте их документацию и поведение на staging. В некоторых случаях безопаснее ограничить доступ на уровне конкретных ролей, а не по принципу «всем кроме авторизованных».
Итоговая логика простая: не отключайте REST API целиком, если не уверены в последствиях. Сначала найдите лишние маршруты, потом закройте их точечно и обязательно проверьте фронтенд, админку и логи после внедрения.