Карта дня, май, перетяжка
Карта дня, май, перетяжка
Карта дня, май, перетяжка

CORS от А до Я: история, ошибки и грамотная настройка

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

1К открытий3К показов
CORS от А до Я: история, ошибки и грамотная настройка

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) разрешены.

Пример ответа сервера с разрешением:

			Access-Control-Allow-Origin: https://frontend.com  
Access-Control-Allow-Methods: GET, POST  
Access-Control-Allow-Headers: Content-Type
		

Топ-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, добавьте:

			const cors = require('cors');
app.use(cors({
  origin: 'http://example.com'
}));
		

Браузер блокирует 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:

			app.use(cors({
  origin: 'http://example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));
		

Запросы с учётом сессий (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

			app.use(cors({
  origin: 'http://example.com',
  credentials: true
}));
		

На сервере также добавьте:

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, добавьте:

			fetch(url, {
  redirect: 'follow'
});
		

Неверно настроен Access-Control-Allow-Headers

Сообщение об ошибке:

"Request header field <X> is not allowed by Access-Control-Allow-Headers"

Ваш запрос содержит нестандартный заголовок (например, Authorization), а сервер не пропускает его.

Решение:

На сервере укажите, какие заголовки разрешены:

			app.use(cors({
  origin: 'http://example.com',
  allowedHeaders: ['Content-Type', 'Authorization']
}));
		

Метод запроса запрещён сервером

Сообщение об ошибке:

"Method PUT is not allowed by Access-Control-Allow-Methods"

Вы отправляете запрос методом PUT, PATCH, DELETE, а сервер разрешает только GET и POST.

Решение:

На сервере явно разрешите нужные методы:

			app.use(cors({
  origin: 'http://example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE']
}));
		
Следите за новыми постами
Следите за новыми постами по любимым темам
1К открытий3К показов