Если браузер сообщает об ошибке CORS, а OPTIONS возвращает 401 или 403, сначала проверьте предварительный запрос — preflight. Его отказ может остановить отправку основного запроса: успешный вызов API из терминала этого не опровергает.
Задача диагностики — найти, кто отклонил OPTIONS, и сверить разрешения для конкретной страницы, метода и заголовков. Не отключайте авторизацию API: разрешение предварительной проверки не даёт пользователю права читать или менять данные.
1. Найдите именно предварительный запрос
В инструментах разработчика откройте Network до повторения ошибки, включите сохранение журнала и найдите URL API. Смотрите все типы запросов: фильтр только по Fetch/XHR может скрыть нужную строку.
Зафиксируйте четыре значения:
- адрес запроса;
Origin— схему, домен и порт страницы, которая обращается к API;Access-Control-Request-Method— метод будущего запроса;Access-Control-Request-Headers— имена заголовков, которые браузер хочет отправить.
Например, POST с JSON на другой origin обычно требует preflight. Сам OPTIONS не содержит JSON заказа: браузер предварительно спрашивает сервер о разрешении. Этот механизм описан в справке MDN о preflight.
Если основной запрос уже виден и получил ответ, рассматривайте его отдельно: причина может быть в CORS-заголовках ответа или авторизации самой операции. Отсутствие OPTIONS тоже не доказывает сбой: предварительная проверка нужна не всегда, а её успешный результат браузер может кешировать.
2. По ответу определите следующий шаг
| Ответ OPTIONS | Что проверить | |---|---| | 401 | Не требует ли шлюз или middleware входа до обработки preflight | | 403 | Правила разрешённых origin, ограничения WAF и журнал отказов | | 404 или 405 | Дошёл ли OPTIONS до нужного маршрута и кто запретил метод | | 301 или 302 | Нет ли перенаправления на вход, HTTPS или другой путь | | 200 или 204 | Совпадают ли CORS-разрешения с запросом браузера |
Код — направление поиска, а не доказательство причины. Сопоставьте время и путь с журналами CDN, прокси и приложения: запись на внешнем слое без записи в приложении помогает сузить участок отказа. Если запрещён сам метод, пригодится отдельный разбор ошибки 405.
Предварительный CORS-запрос не должен зависеть от пользовательских cookie или bearer-токена. Для разрешённых origin обработку OPTIONS настраивают до проверки сессии, сохраняя авторизацию основного запроса. При намеренном запрете чужого origin отказ исправлять не требуется.
3. Сверьте разрешения, даже если код успешный
Для примера: страница https://app.example.com хочет отправить POST с заголовками authorization и content-type. Проверяйте ответ API по списку:
Access-Control-Allow-Originсодержит разрешённый origin страницы; различия в схеме и порте существенны.Access-Control-Allow-Methodsразрешает нужный метод.Access-Control-Allow-Headersразрешает запрошенные заголовки;Authorizationуказывают явно.- Если основной fetch использует
credentials: "include", нужныAccess-Control-Allow-Credentials: trueи конкретный origin вместо*.
Не отражайте произвольный Origin без проверки по списку разрешений. Посмотрите, не добавляют ли приложение и прокси два конфликтующих значения Access-Control-Allow-Origin. Если значение выбирается по Origin и ответ кешируется, проверьте корректное разделение кеша через Vary: Origin.
Успешного HTTP-кода без подходящих разрешений недостаточно; правила проверки определяет стандарт Fetch. CORS-заголовки потребуются и в ответе на основной запрос, который должен быть доступен JavaScript.
4. Воспроизведите OPTIONS без секретов
На своём API можно отдельно посмотреть HTTP-ответ. Ниже условные адреса: замените их и перечень заголовков значениями из Network.
curl -i -X OPTIONS 'https://api.example.com/items' \
-H 'Origin: https://app.example.com' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: authorization,content-type'
Здесь указано только имя authorization, а не токен. Cookie, пароль и тело пользовательской операции не нужны. Не добавляйте -L: сначала важно увидеть исходный редирект, если он есть.
curl показывает серверный ответ, но не применяет браузерную политику CORS. Поэтому совпадение кодов полезно для сравнения, а окончательная проверка проходит в браузере с исходной страницы. Переключение fetch в no-cors не делает JSON доступным скрипту; ограничения и работу с credentials поясняет руководство MDN по CORS.
5. Подтвердите исправление на обоих этапах
После изменения повторите проверку в новом браузерном сеансе, чтобы старое разрешение preflight не скрыло результат. Используйте тестовую операцию без реального заказа, оплаты или отправки письма.
Исправление подтверждено, когда разрешённая страница проходит OPTIONS, основной запрос отправляется и JavaScript получает ожидаемый результат. Отдельно убедитесь, что без необходимых прав основной запрос по-прежнему отклоняется. Сохраните время проверки, origin, метод и обезличенные заголовки ответа для сравнения после следующего релиза.
Web-Puls помогает контролировать внешнюю доступность безопасного публичного URL, но такая проверка не воспроизводит CORS-контекст страницы. Выбор контрольной точки разобран в руководстве по мониторингу API.
Если отказ возникает между прокси и приложением и причину не удаётся локализовать, можно отправить заявку на диагностику. Укажите публичный URL без секретов, время ошибки, origin, код OPTIONS и недавние изменения; это даст исходные данные для обсуждения задачи.