Как отлаживать ошибки CORS в браузере
Вы инициируете fetch-запрос, открываете консоль и видите что-то вроде:
Access to fetch at ‘https://api.example.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.
Прежде чем начать бездумно менять заголовки на сервере, остановитесь. Большинство проблем с отладкой CORS в браузере сводится к чтению того, что браузер уже вам сообщает — если знать, куда смотреть.
Ключевые моменты
- Ошибки CORS контролируются браузером, а не сервером. Запрос часто достигает сервера; блокировка происходит при получении ответа.
- Сообщение в консоли браузера — ваш основной диагностический инструмент. Оно часто указывает на проблемное правило или заголовок, но может также отражать проблемы с сетью, TLS или политиками безопасности.
- Preflight-запросы OPTIONS завершаются неудачей незаметно, если вы их не ищете. Всегда проверяйте вкладку Network с фильтром “All”, чтобы их обнаружить.
- Не каждая ошибка, похожая на CORS, на самом деле является проблемой CORS. Смешанный контент, сбои TLS, редиректы и расширения браузера могут маскироваться под проблемы CORS.
Что на самом деле означают ошибки CORS
Ошибки CORS (Cross-Origin Resource Sharing) возникают, когда браузер блокирует кросс-доменный запрос, потому что ответ сервера не содержал правильных разрешений. Ключевое слово здесь — браузер. Запрос часто достигает сервера. Блокировка происходит на обратном пути.
Origin (источник) определяется комбинацией протокола, домена и порта. Таким образом, http://localhost:3000 и http://localhost:4000 — это разные источники, даже на одной машине.
Как отлаживать ошибки CORS с помощью DevTools
Откройте DevTools → вкладка Network (F12 или Cmd+Option+I). Воспроизведите неудачный запрос. Затем:
- Найдите неудачный запрос — ищите красную запись или отфильтруйте по “Fetch/XHR”.
- Проверьте вкладку Console — сообщение об ошибке там конкретное. Оно сообщает вам, какой заголовок отсутствует или не соответствует.
- Кликните на запрос → вкладка Headers — изучите как заголовки запроса (особенно
Origin), так и заголовки ответа (особенноAccess-Control-Allow-Origin).
Ошибка в консоли — ваш основной диагностический инструмент. Современные Chrome и Firefox выдают точные сообщения, например:
- “No ‘Access-Control-Allow-Origin’ header is present” → сервер вообще не отправил заголовки CORS.
- “The value of ‘Access-Control-Allow-Origin’ must not be the wildcard ’*’ when credentials mode is ‘include’” → вы используете
credentials: 'include', но сервер ответил с*. - “Method PUT is not allowed by Access-Control-Allow-Methods” → preflight-запрос отклонил ваш HTTP-метод.
Отладка preflight-запросов (OPTIONS)
Когда ваш запрос использует не-простой метод (PUT, DELETE, PATCH), пользовательские заголовки или тип контента, отличный от нескольких разрешённых спецификацией, браузер сначала отправляет preflight-запрос OPTIONS. Если он терпит неудачу, ваш основной запрос никогда не отправляется.
На вкладке Network отфильтруйте по “All” и ищите OPTIONS-запрос к тому же URL, появляющийся непосредственно перед вашим основным запросом. Кликните на него и проверьте:
- Заголовки запроса:
Access-Control-Request-MethodиAccess-Control-Request-Headers— они сообщают серверу, что требуется для реального запроса. - Заголовки ответа:
Access-Control-Allow-Methods,Access-Control-Allow-Headers,Access-Control-Allow-Origin— сервер должен явно разрешить каждый из них.
Если OPTIONS-запрос возвращает статус-код 4xx или 5xx, это, вероятно, ошибка маршрутизации или конфигурации на стороне сервера, а не неправильная настройка заголовков CORS. Сначала исправьте основную ошибку.
Discover how at OpenReplay.com.
Это действительно ошибка CORS?
Не каждая ошибка, проявляющаяся как CORS, вызвана CORS. Распространённые ложные срабатывания включают:
- Смешанный контент — загрузка
http://со страницыhttps://блокируется политикой смешанного контента браузера, независимо от заголовков CORS. - Ошибки TLS/сертификата — неудачное TLS-рукопожатие может выглядеть как сбой CORS в консоли.
- Редиректы на другой источник — если сервер перенаправляет ваш запрос на новый источник, браузер блокирует запрос. Кросс-доменные редиректы не разрешены для CORS-запросов.
- Расширения браузера — расширения для конфиденциальности или блокировки рекламы могут отменять запросы до того, как CORS будет проверен.
- Ограничения на сторонние cookie — если вы используете
credentials: 'include', современные браузеры могут блокировать cookie от сторонних источников, даже когда ваши заголовки CORS абсолютно корректны.
Чтобы выявить реальную причину, попробуйте выполнить запрос в приватном окне/режиме инкогнито с отключёнными расширениями.
Запросы с учётными данными: распространённая ловушка
Если вы используете fetch(url, { credentials: 'include' }), сервер не может ответить с Access-Control-Allow-Origin: *. Он должен вернуть точный запрашивающий источник. Серверу также необходимо включить Access-Control-Allow-Credentials: true в свой ответ. Пропустите любой из них — и браузер заблокирует ответ.
Быстрый чек-лист отладки CORS
| Проверка | На что обратить внимание |
|---|---|
| Сообщение об ошибке в консоли | Точный заголовок или правило, которое не сработало |
| Присутствует OPTIONS-запрос? | Preflight был инициирован — проверьте его ответ |
Ответ содержит Access-Control-Allow-Origin? | Должен совпадать с вашим источником или быть * |
| Используете credentials? | * не разрешён; требуется точный источник |
| 4xx/5xx в запросе? | Ошибка сервера — не проблема CORS |
| Смешанный контент? | HTTP со страницы HTTPS — всегда блокируется |
Заключение
Сообщение об ошибке браузера, вкладка Network и панель Headers предоставляют вам всё необходимое для диагностики большинства проблем CORS менее чем за пять минут. Прочитайте точную ошибку, найдите OPTIONS-запрос, если он существует, сравните то, что было отправлено, с тем, что ожидалось, и действуйте исходя из этого. Сопротивляйтесь желанию слепо добавлять Access-Control-Allow-Origin: * на ваш сервер — сначала поймите, что запрашивает браузер, и отвечайте точно.
Часто задаваемые вопросы
Нет. CORS контролируется браузером на основе заголовков, которые отправляет сервер. Фронтенд не может это переопределить. Если вы не контролируете сервер, вы можете направлять запросы через бэкенд-прокси, который добавляет правильные заголовки, но код на стороне браузера сам по себе не может обойти это ограничение.
Postman — это не браузер, и он не применяет политику одного источника (same-origin policy). Он отправляет запросы напрямую, не проверяя заголовки CORS в ответе. Браузеры применяют CORS для защиты пользователей, поэтому запрос, который успешен в Postman, всё равно может быть заблокирован браузером, если сервер не включил необходимые заголовки ответа.
Preflight инициируется, когда запрос использует метод, отличный от GET, HEAD или POST, включает пользовательские заголовки или устанавливает Content-Type, отличный от application/x-www-form-urlencoded, multipart/form-data или text/plain. Браузер сначала отправляет OPTIONS-запрос, чтобы подтвердить, что сервер разрешает фактический запрос.
Если ваш запрос включает учётные данные, такие как cookie или заголовки авторизации, сервер не может использовать значение wildcard. Он должен ответить с точным источником, который сделал запрос, а также включить заголовок Access-Control-Allow-Credentials, установленный в true. Wildcard действителен только для запросов без учётных данных.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.