Stage 6. CORS Fundamentals
CORS — это механизм браузерной безопасности, который решает, может ли JavaScript одного origin читать ответ от другого origin. Ключевая мысль: CORS не защищает API “в целом”, он управляет именно тем, что браузер разрешит или запретит фронтенд-коду. Поэтому ситуация, когда сервер вернул 200, но приложение на странице не получило доступ к данным, абсолютно реальна и встречается очень часто.
Чтобы понять CORS, сначала нужно чётко понимать origin: это комбинация scheme + host + port. Если хотя бы один элемент отличается, браузер считает источники разными. Из-за этого https://app.example.com и https://api.example.com — это разные origin, даже если у них общий домен второго уровня.
В простых случаях браузер может сразу отправить основной запрос. В более сложных отправляется preflight — предварительный OPTIONS-запрос, где браузер спрашивает сервер: “Разрешены ли метод, заголовки и origin для этого вызова?”. Если ответ не содержит корректные Access-Control-Allow-*, браузер блокирует доступ к результату.
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type
Частая ошибка — считать, что CORS можно “разрешить один раз и забыть”. На практике политика зависит от окружения, доменных схем, auth-модели и типа клиента. Слишком широкая политика в production повышает риск, слишком узкая ломает пользовательские сценарии после деплоя. Правильный подход — задавать минимально необходимую политику и контролировать её через тесты.
Особенно важен нюанс с credentials. Если фронтенд отправляет cookie или иные credentialed-запросы, нельзя использовать Access-Control-Allow-Origin: * вместе с Access-Control-Allow-Credentials: true. В этом случае сервер должен явно вернуть конкретный origin. Это не “ограничение ради ограничения”, а защита от небезопасного cross-site доступа.
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
Access-Control-Allow-Headers: Authorization,Content-Type
Access-Control-Allow-Credentials: true
Важно также отличать CORS от CSRF. CORS контролирует, может ли браузерный JavaScript читать ответ. CSRF защищает от нежелательных state-changing запросов от имени пользователя. Эти механизмы дополняют друг друга, но решают разные задачи. Если их смешивать, архитектура безопасности получается ложной и уязвимой.
Для диагностики CORS нельзя смотреть только backend-логи. Нужно анализировать в DevTools пару запросов: preflight и основной. Именно на уровне браузера видно, почему ответ заблокирован, даже если сервер считает вызов успешным. Это критическая практика для любой команды, которая поддерживает web-клиент.
Практический сценарий
После переноса фронтенда на новый домен часть пользователей перестала видеть данные профиля. API отвечал 200, алертов по backend не было. Разбор в DevTools показал, что preflight-запросы не получали разрешение на Authorization заголовок. Исправление заняло 10 минут, но до этого команда потратила день на проверку “бизнес-логики”. Этот кейс хорошо показывает: CORS — это отдельный уровень контракта, который нужно проектировать и тестировать так же серьёзно, как и endpoint-логику.