CORS от А до Я: история, ошибки и грамотная настройка
Механизм CORS не даёт вашему фронтенду работать с API? Разбираем, как он устроен, что означают частые ошибки и как правильно его настраивать в проде.
1К открытий3К показов

CORS (Cross-Origin Resource Sharing) — это механизм, который позволяет веб-приложениям безопасно запрашивать ресурсы с других доменов. Если вы когда-либо видели в консоли браузера ошибку вроде «No ‘Access-Control-Allow-Origin’ header», значит, вы уже столкнулись с его работой. Сегодня разберёмся, как появился CORS, зачем он нужен и как с ним работать.
Откуда возник CORS
Всё началось в начале 2000-х, когда веб-приложения становились всё сложнее, а разработчики чаще нуждались в данных с других доменов. Однако браузеры строго придерживались политики одного источника (Same-Origin Policy, SOP), запрещавшей доступ к сторонним ресурсам. Эта политика появилась ещё в 1995 году в Netscape Navigator 2.0 и была призвана защитить пользователей от атак вроде XSS. Проблема в том, что SOP блокировала не только вредоносные, но и вполне легитимные запросы, например, к внешним API. Разработчики пытались обойти ограничения с помощью JSONP или iframe, но такие решения были небезопасными и нестабильными.
В 2005 году инженер Мэтт Ошри предложил идею более надёжного и безопасного механизма, который в дальнейшем стал основой CORS. А в 2008-м Анн ван Кестерен подготовила черновик спецификации для W3C. В 2014 году CORS официально вошёл в стандарт Fetch API и с тех пор стал ключевым элементом работы с кросс-доменными запросами. Он помогает контролировать доступ к ресурсам, сохраняя баланс между открытостью веба и безопасностью пользователей.
Как устроен CORS
CORS работает как диалог между браузером и сервером, основанный на HTTP-заголовках. Когда фронтенд-приложение (например, на сайте frontend.com
) запрашивает данные с другого домена (api.com
), браузер автоматически добавляет к запросу заголовок Origin: https://frontend.com
. Этот заголовок сообщает серверу, откуда пришёл запрос.
Сервер в ответ решает, разрешать ли доступ. Если он включает в ответ заголовок Access-Control-Allow-Origin
и указывает там адрес запроса (например, https://frontend.com
) или символ * (разрешение для всех доменов), то браузер пропускает ответ. Если такого заголовка нет или значение не совпадает, браузер блокирует ответ — даже если сервер технически его отправил.
Для так называемых «сложных» запросов, например, с методом POST или нестандартными заголовками, браузер сначала отправляет предварительный (OPTIONS
) запрос, чтобы уточнить у сервера, допустим ли основной. В ответе сервер указывает, какие методы (Access-Control-Allow-Methods
) и заголовки (Access-Control-Allow-Headers
) разрешены.
Пример ответа сервера с разрешением:
Топ-7 самых частых ошибок в CORS и как их исправить
Для того чтобы определить оптимальные методы работы, лучше учиться на ошибках. Хорошая новость в том, что почти все CORS-ошибки легко понять и исправить — если знать, где искать. Ниже собрали 7 самых распространённых ошибок CORS, с которыми сталкиваются разработчики, и рассказали, как можно с ними справиться без боли и паники.
Нет заголовка Access-Control-Allow-Origin
Сообщение об ошибке:
"No ‘Access-Control-Allow-Origin’ header is present on the requested resource"
Когда ваш JavaScript-код пытается получить данные с другого домена, браузер ожидает, что в ответе будет заголовок Access-Control-Allow-Origin
. Если его нет — браузер блокирует ответ, считая его небезопасным.
Решение 1: Настроить сервер на разрешение CORS
Если вы контролируете сервер, добавьте в ответ заголовок:
Access-Control-Allow-Origin: *
* — разрешение запросов с любого домена
Но для безопасности лучше указать конкретный домен:
Access-Control-Allow-Origin: http://example.com
Решение 2: middleware CORS в Express.js
Если вы используете Node.js с Express, добавьте:
Браузер блокирует preflight-запросы (OPTIONS)
Сообщение об ошибке:
"CORS policy preflight request did not succeed"
или что-то похожее.
Иногда перед настоящим запросом браузер сначала отправляет preflight-запрос методом OPTIONS
, чтобы проверить, разрешен ли основной запрос. Это происходит, если:
- Метод запроса не
GET
илиPOST
(например,PUT
,DELETE
).
- В заголовках есть что-то нестандартное (например,
Authorization
илиContent-Type: application/json
).
Если сервер не умеет обрабатывать OPTIONS
— все ломается.
Решение: Обработать OPTIONS-запросы на сервере
Пример для Express.js:
app.options('*', cors()); // Разрешаем preflight-запросы
Или настройка CORS с поддержкой OPTIONS
:
Запросы с учётом сессий (credentials) блокируются
Сообщение об ошибке:
"The value of the ‘Access-Control-Allow-Origin’ header in the response must not be '*' when the request's credentials mode is 'include'"
Вы отправляете запрос с куками или авторизацией (credentials: 'include'
), но сервер разрешает доступ всем (Access-Control-Allow-Origin: *
). Это запрещено — нельзя слать credentials
, если разрешено всем.
Решение: Указать конкретный домен и разрешить credentials
На сервере также добавьте:
Access-Control-Allow-Credentials: true
Смешанные протоколы — HTTP и HTTPS
Сообщение об ошибке:
"Mixed content: The page at 'https://...' was loaded over HTTPS, but requested an insecure resource 'http://...'."
Ваш сайт работает по HTTPS, а API — по HTTP. Браузеры считают, что это опасно, и банально блокируют запросы.
Решение:
- Переведите API на HTTPS.
- Обновите все ссылки с http:// на https://.
- Если работаете локально и сервер только HTTP, используйте ngrok, чтобы создать HTTPS-туннель.
CORS блокирует редиректы
Сообщение об ошибке:
"Redirect from '...api' to '...login' has been blocked by CORS policy."
API делает редирект, но в ответе на редирект нет CORS-заголовков. Браузер не знает, можно ли доверять редиректу, и блокирует.
Решение:
Убедитесь, что редирект тоже возвращает правильные заголовки:
Access-Control-Allow-Origin: http://example.com
Если используете fetch
, добавьте:
Неверно настроен Access-Control-Allow-Headers
Сообщение об ошибке:
"Request header field <X> is not allowed by Access-Control-Allow-Headers"
Ваш запрос содержит нестандартный заголовок (например, Authorization), а сервер не пропускает его.
Решение:
На сервере укажите, какие заголовки разрешены:
Метод запроса запрещён сервером
Сообщение об ошибке:
"Method PUT is not allowed by Access-Control-Allow-Methods"
Вы отправляете запрос методом PUT
, PATCH
, DELETE
, а сервер разрешает только GET
и POST
.
Решение:
На сервере явно разрешите нужные методы:
1К открытий3К показов