Ошибка CORS означает, что браузер не разрешил JavaScript прочитать ответ другого origin по действующей политике сервера. Чаще всего исправление находится в настройке API или доверенного прокси, а не в строке fetch. Сначала запишите origin страницы и origin API, затем найдите запрос во вкладке Network. Проверьте, отправлялся ли OPTIONS, какой заголовок Origin ушёл и какие Access-Control-Allow-* вернул сервер.
CORS расшифровывается как Cross-Origin Resource Sharing. Это HTTP-механизм, с помощью которого сервер сообщает браузеру, каким внешним источникам разрешён доступ к ответу. Запрос может физически дойти до API и даже получить 200, но браузер всё равно не отдаст тело вашему JavaScript, если политика не совпала.
Схема, хост и порт должны совпасть, иначе запрос считается cross-origin.
Посмотреть реальный запрос, preflight и ответные заголовки.
Разрешение обычно настраивается на API, gateway или backend-прокси.
Что браузер считает другим origin
Origin состоит из схемы, имени хоста и порта. Страницы http://localhost:5173 и http://localhost:3000 находятся на одном компьютере, но имеют разные порты и потому разные origin. Путь не входит в origin: /users и /orders на одном протоколе, хосте и порту остаются same-origin.
Страница: http://localhost:5173
API: http://localhost:3000
Схема: http = http
Хост: localhost = localhost
Порт: 5173 != 3000
Результат: cross-origin request| Страница | API | Результат |
|---|---|---|
https://site.test | https://site.test/api | Same-origin |
https://site.test | http://site.test/api | Другая схема |
https://site.test | https://api.site.test | Другой хост |
http://localhost:5173 | http://localhost:3000 | Другой порт |
https://site.test/a | https://site.test/b | Same-origin, пути не важны |

Различие любого из трёх компонентов делает запрос cross-origin.
Как читать CORS-ошибку во вкладке Network
- Откройте DevTools, вкладку Network и включите сохранение лога при перезагрузке.
- Повторите действие и найдите запрос к API по URL или методу.
- Запишите
Originиз Request Headers. - Проверьте
Access-Control-Allow-Originв Response Headers. - Если перед запросом есть
OPTIONS, изучите его статус и заголовки отдельно. - Сравните разрешённые методы, заголовки и credentials с реальным запросом.
Для некоторых cross-origin запросов браузер сначала отправляет preflight методом OPTIONS. Он спрашивает, разрешены ли origin, метод и нестандартные заголовки будущего запроса. Если preflight не прошёл, основной POST или PUT может вообще не уйти. Поэтому смотреть только логи обработчика POST недостаточно.
OPTIONS /api/tasks HTTP/1.1
Origin: http://localhost:5173
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Основной запрос уходит только после успешной проверки разрешений, если preflight необходим.
Если запроса нет, ищите ошибку JavaScript или неверный URL. Если OPTIONS возвращает 404 или 500, настройте маршрут preflight. Если ответ есть, но нужного разрешающего заголовка нет, исправление принадлежит серверу или промежуточному прокси.
Какие заголовки должен вернуть сервер
Access-Control-Allow-Origin указывает разрешённый origin. Для credentialed-запросов нельзя сочетать cookies и wildcard *: сервер должен вернуть конкретный origin и Access-Control-Allow-Credentials: true. Разрешённые методы и заголовки должны покрывать фактический запрос, особенно на preflight.
| Заголовок ответа | Назначение | Частая ошибка |
|---|---|---|
Access-Control-Allow-Origin | Какой origin может читать ответ | Нет точного origin или поставлен список в одной строке |
Access-Control-Allow-Methods | Допустимые методы | Не указан PUT, PATCH или DELETE |
Access-Control-Allow-Headers | Допустимые request headers | Не разрешён Content-Type или Authorization |
Access-Control-Allow-Credentials | Разрешить cookies и credentials | Использован wildcard origin |
Vary: Origin | Разделить кэш по origin | CDN отдаёт заголовок другого клиента |
fetch('https://api.example.test/profile', {
credentials: 'include',
})
// Сервер должен ответить, например:
// Access-Control-Allow-Origin: https://app.example.test
// Access-Control-Allow-Credentials: true
// Vary: Origin
Разрешение возвращает владелец API или доверенный прокси, а браузер проверяет его.
Почему no-cors, расширение и случайный прокси не решают проблему
Режим no-cors создаёт непрозрачный ответ: JavaScript не получает нормальный статус, заголовки и тело. Он подходит для узких сценариев вроде отправки некоторых ресурсов, но не превращает закрытый API в доступный. Расширение, отключающее защиту, меняет только ваш браузер и не исправляет приложение для пользователей.
Ответ становится opaque, и код не сможет прочитать JSON. Проблема лишь меняет форму.
Локальная демонстрация скрывает реальный дефект и создаёт небезопасную среду.
Заголовок разрешения должен находиться в ответе API, а не в исходящем fetch.
Wildcard несовместим с credentialed CORS и создаёт неверную модель доверия.
После настройки откройте приложение в чистом профиле без расширений. В Network должны быть успешный preflight при необходимости и основной ответ с точным origin. В Console не должно оставаться сообщения CORS.
Кто должен исправлять CORS в проекте
Если вы контролируете API, добавьте allowlist origin в backend или API gateway. Если API чужой и не поддерживает браузерные запросы, используйте собственный серверный маршрут, который обращается к нему по официальным условиям. В разработке dev-server proxy может сохранить same-origin для браузера, но продовая архитектура должна иметь такой же понятный маршрут.
- Записаны точные origin фронтенда и API, включая схему и порт.
- В Network отдельно проверены OPTIONS и основной запрос.
- Сервер возвращает конкретный разрешённый origin.
- Метод и пользовательские заголовки разрешены preflight-ответом.
- Cookies используются только вместе с credentials и явным origin.
- Решение проверено без расширений и отключения безопасности браузера.
Из чего состоит origin?
Из схемы, хоста и порта. Путь и query-строка в origin не входят.
Почему API вернул 200, а JavaScript не видит ответ?
Браузер может получить сетью ответ, но не открыть его коду из-за отсутствующего или неверного CORS-заголовка.
Что такое preflight?
Предварительный OPTIONS-запрос, которым браузер проверяет разрешение origin, метода и заголовков.
Поможет ли mode no-cors прочитать JSON?
Нет. Ответ станет непрозрачным, и JavaScript не получит обычный доступ к телу.
Можно ли использовать звёздочку вместе с cookies?
Для credentialed CORS сервер должен вернуть конкретный origin, а не wildcard.
Потренируйтесь отправлять запросы в курсе JavaScript, затем откройте пошаговый разбор API на JavaScript. Для работы с элементами результата пригодится статья про DOM.
Если запрос запускается по кнопке, проверьте цепочку по материалу о багах JavaScript-кнопок. В Кодике удобно сохранять origin, заголовки и статус каждого шага как короткий воспроизводимый пример.