Если REST API WordPress внезапно отвечает 403 Forbidden только на запросы с Authorization, проблема обычно не в самом WordPress, а в связке сервер + прокси + безопасность + плагин. На практике это ломает мобильные приложения, интеграции с внешними сервисами, headless-фронтенд и любые запросы по Basic Auth или Bearer-токену.
Ниже — рабочий порядок диагностики и исправления без гадания. Сначала нужно понять, где именно режется заголовок: на уровне веб-сервера, PHP-FPM, плагина безопасности или самого маршрута REST API.
Как выглядит проблема на практике
Типичный сценарий: обычный запрос к /wp-json/wp/v2/posts работает, а запрос с авторизацией возвращает 403 или 401. Иногда в ответе есть текст вроде rest_not_logged_in, rest_cannot_create или просто пустой JSON с ошибкой. В логах при этом может быть тишина, потому что запрос не доходит до нужного обработчика.
Особенно часто это всплывает после:
- переноса сайта на новый хостинг;
- установки плагина безопасности или WAF;
- включения reverse proxy, Cloudflare или nginx-кеша;
- обновления конфигурации Apache/nginx;
- перехода на Basic Auth для внешней интеграции.
Диагностика: где именно ломается Authorization
Проверять нужно по слоям. Если сразу менять код WordPress, можно потратить время и ничего не исправить.
1. Сравните запросы с заголовком и без него
Сделайте два запроса к одному и тому же endpoint. Удобнее всего через curl:
curl -i https://example.com/wp-json/wp/v2/postscurl -i -u username:application-password https://example.com/wp-json/wp/v2/postsЕсли первый запрос проходит, а второй получает 403, это уже хороший признак: маршрут REST API жив, а проблема связана именно с авторизацией или обработкой заголовка.
2. Посмотрите, доходит ли заголовок до PHP
Иногда сервер просто не передаёт Authorization в PHP. Для быстрой проверки можно временно повесить небольшой mu-plugin или вставить код в тестовый плагин:
<?php
add_action( 'init', function () {
if ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
error_log( 'HTTP_AUTHORIZATION: ' . $_SERVER['HTTP_AUTHORIZATION'] );
}
if ( isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) {
error_log( 'REDIRECT_HTTP_AUTHORIZATION: ' . $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] );
}
} );Если в логах пусто, а клиент точно отправляет заголовок, проблема на стороне веб-сервера или прокси.
3. Отключите плагины безопасности точечно
Не нужно сразу деактивировать всё подряд на боевом сайте. Достаточно временно отключить плагины, которые фильтруют REST API, XML-RPC, авторизацию или подозрительные заголовки. Если после этого 403 исчезает, источник найден.
Чаще всего мешают:
- плагины с защитой REST API;
- firewall-модули;
- антибот-защита;
- плагины, которые ограничивают доступ к
/wp-json/для гостей.
Пошаговое решение
Шаг 1. Исправьте передачу Authorization на сервере
Для Apache часто помогает правило в .htaccess, если сервер не прокидывает заголовок в PHP:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>Для nginx обычно нужно убедиться, что FastCGI получает заголовок. В конфигурации PHP-локации это выглядит так:
fastcgi_param HTTP_AUTHORIZATION $http_authorization;Если сайт работает через proxy, проверьте, не обрезает ли промежуточный слой этот заголовок. На практике именно здесь чаще всего и сидит причина.
Шаг 2. Проверьте правила безопасности и WAF
Если заголовок до PHP доходит, но REST API всё равно отвечает 403, смотрите в сторону фильтрации на уровне WordPress. Временно отключите:
- правила, блокирующие
/wp-json/; - ограничение доступа к API для неавторизованных пользователей;
- защиту от «подозрительных» заголовков;
- кастомные
wp_die()вrest_authentication_errors.
Если вы пишете свой код, не блокируйте REST API глобально. Лучше ограничивать только конкретные маршруты и только после проверки прав пользователя.
Шаг 3. Используйте правильный способ авторизации
Для внешних интеграций в WordPress безопаснее и проще использовать Application Passwords, если задача не требует полноценного OAuth. Это штатный механизм WordPress, который работает через REST API и не требует самописной схемы авторизации.
Пример запроса:
curl -u username:application-password \
https://example.com/wp-json/wp/v2/users/meЕсли вы используете Bearer-токен через кастомный плагин, убедитесь, что:
- заголовок не режется на сервере;
- плагин действительно подключается до обработки REST-запроса;
- ответы на ошибки не маскируются общим 403 без пояснений.
Шаг 4. Если нужен свой REST endpoint, проверяйте права явно
Ниже пример корректной регистрации маршрута с проверкой прав. Здесь нет магии: сначала проверка, потом действие.
<?php
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/sync', [
'methods' => 'POST',
'callback' => 'myplugin_sync_handler',
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
] );
} );
function myplugin_sync_handler( WP_REST_Request $request ) {
return rest_ensure_response( [
'ok' => true,
'message' => 'Синхронизация разрешена',
] );
}Если permission_callback отсутствует или возвращает неверное значение, WordPress может отдавать 403 даже тогда, когда запрос технически дошёл до маршрута.
Что проверить после исправления
Исправление считается рабочим только если вы проверили не один запрос, а весь сценарий целиком.
- запрос с
Authorizationпроходит без 403; - ответ содержит ожидаемые данные, а не только код 200;
- лог сервера больше не показывает обрезанный заголовок;
- авторизация работает и для GET, и для POST, если это нужно вашему сценарию;
- кеш не подменяет ответ старой ошибкой;
- плагин безопасности не блокирует только часть маршрутов.
Для быстрой проверки удобно сравнить ответ до и после через curl -i и убедиться, что в заголовках и теле нет признаков старой ошибки.
Сравнение подходов: плагин, сервер, код
| Подход | Когда подходит | Минус |
|---|---|---|
| Правка конфигурации сервера | Если Authorization не доходит до PHP | Нужен доступ к nginx/Apache и аккуратность при деплое |
| Отключение/настройка security-плагина | Если блокировка идёт на уровне WordPress | Можно случайно ослабить защиту шире, чем нужно |
| Правка кода REST endpoint | Если 403 вызван логикой permission_callback | Требует понимания прав и ролей |
Частые ошибки и как их исправить
Отключили кеш, но проблема осталась
Кеш не всегда виноват. Если заголовок не доходит до PHP, отключение кеша ничего не изменит. Сначала проверяйте сервер и прокси, потом уже плагины оптимизации.
Добавили правило в .htaccess, но сайт на nginx
Это частая ошибка при переносе между хостингами. .htaccess работает только в Apache. На nginx нужно править конфигурацию виртуального хоста или параметры FastCGI.
Сделали глобальный bypass для REST API
Иногда в коде пишут слишком широкое исключение, и API становится доступным там, где не должен. Лучше ограничивать исключение конкретным маршрутом или конкретной ролью пользователя.
Проверяли только в браузере
Браузерные запросы и серверные запросы — не одно и то же. Для диагностики авторизации используйте curl или Postman, иначе можно пропустить проблему с заголовком.
Безопасность и производительность
Если вы открываете доступ к REST API для внешней системы, не компенсируйте проблему полным снятием защиты. Лучше:
- использовать Application Passwords или другой штатный механизм авторизации;
- ограничить права пользователя минимально необходимыми capabilities;
- не логировать секреты в debug.log;
- не отключать REST API для всех, если нужен только один маршрут;
- проверить, не кешируется ли ответ с ошибкой на уровне CDN.
Если на сайте много технических дублей, мусорных архивов и лишних endpoint-ов, имеет смысл отдельно посмотреть на чистку и SEO-ограничения. В таких задачах иногда помогает аккуратная настройка плагина вроде Clearfy Pro, но только если вы понимаете, что именно отключаете и зачем.
Когда проблема не в WordPress
Если после всех проверок 403 остаётся только для запросов с авторизацией, а без неё всё работает, почти наверняка виноваты серверные правила или промежуточный фильтр. В этом случае полезно смотреть:
- логи nginx/Apache;
- логи WAF или Cloudflare;
- правила mod_security;
- настройки reverse proxy;
- кастомные фильтры хостинга.
Именно здесь чаще всего находится причина, которую не видно из админки WordPress. Когда заголовок начинает доходить до PHP и права маршрута настроены корректно, REST API обычно перестаёт отдавать 403 без дополнительных обходных решений.