Если вы когда-нибудь писали фронтенд, который обращается к API, скорее всего, сталкивались с загадочной ошибкой:
Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy...
Многие просто добавляют "какой-нибудь заголовок" или ставят прокси - но давайте разберёмся по существу, что такое CORS, зачем он нужен и как правильно с ним работать.
Что такое CORS
CORS (Cross-Origin Resource Sharing) - это механизм безопасности в браузерах, который регулирует доступ веб-страницы к ресурсам с другого домена (origin).
Origin - это комбинация протокола + домена + порта.
Например:
CORS - не библиотека, не протокол, не плагин. Это набор HTTP-заголовков, с помощью которых сервер говорит браузеру, какие источники могут обращаться к его ресурсам.
Проблема, которую решает CORS
Без CORS, любой сайт мог бы делать AJAX-запросы на другой сайт от вашего имени, красть токены и данные, менять настройки и т.д.
Например:
// Пользователь залогинен в bank.com fetch('https://bank.com/transfer', { method: 'POST', body: JSON.stringify({ to: 'attacker', amount: 10000 }) });
Если бы браузер не проверял источник - злоумышленник мог бы разместить этот код где угодно, и запрос отправился бы с вашими cookie.
Именно поэтому браузеры блокируют такие запросы без разрешения сервера - и это называется CORS-политика.
Как работает CORS
Когда фронтенд делает запрос на другой origin, браузер добавляет специальные заголовки и ждёт ответа от сервера, который должен подтвердить, что этот запрос разрешён.
- Простой запрос (Simple Request)
К таким запросам относятся только:
Пример:
fetch('https://api.example.com/users', { method: 'GET' });
Браузер просто отправит запрос, и если сервер вернёт заголовок:
Access-Control-Allow-Origin: https://myfrontend.com
запрос будет разрешён.
Если сервер не вернул этот заголовок, браузер не даст JavaScript-у доступ к ответу, хотя сам запрос на сервер уйдёт.
- Предварительный запрос (Preflight Request)
Если запрос не простой (например, метод PUT, DELETE или есть кастомные заголовки), браузер сначала делает "предварительный" OPTIONS-запрос:
OPTIONS /users HTTP/1.1 Origin: https://myfrontend.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: Content-Type, Authorization
Сервер должен ответить:
HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://myfrontend.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 600
Если браузер получает "OK" - он выполняет основной запрос.
Важные заголовки CORS
Access-Control-Allow-Origin. Разрешённые источники. Можно указать конкретный домен (https://site.com) или * для всех.
Access-Control-Allow-Methods. Какие HTTP-методы разрешены (GET, POST, PUT, DELETE).
Access-Control-Allow-Headers. Разрешённые кастомные заголовки (Authorization, X-Requested-With, и т.д.).
Access-Control-Allow-Credentials. Если true, позволяет передавать cookie и заголовки авторизации.
Access-Control-Expose-Headers. Разрешённые для чтения заголовки ответа.
Access-Control-Max-Age. Время кеширования результата preflight-запроса.
CORS - это не "баг", а фундамент браузерной безопасности.
Он защищает пользователей от того, чтобы злоумышленники не могли выполнять запросы от их имени к сторонним сервисам. Поняв, как он работает, вы перестанете "ставить костыли" и сможете грамотно проектировать клиент-серверное взаимодействие.
Source: Orkhan Alishov's notes