CORS: почему возникает ошибка и как её исправить
Практика3 минРедакция Innovialabs
Коротко
Ошибку CORS выдаёт браузер, а не сервер — она исправляется на сервере, который должен явно разрешить ваш origin нужными заголовками, а не отключением защиты.
Почему появляется ошибка CORS
Браузер следует same-origin policy: JavaScript на странице может свободно читать ответы только со своего origin — сочетания протокола, домена и порта. https://site.uz и https://api.site.uz — разные origin, как и http://localhost:3000 и http://localhost:8080.
CORS (Cross-Origin Resource Sharing) — механизм, через который сервер говорит браузеру: «Этому origin можно читать мои ответы». Если нужного заголовка нет, браузер блокирует ответ и пишет ошибку в консоль.
Важно: запрос часто доходит до сервера и выполняется. Браузер лишь не отдаёт результат вашему коду. Поэтому Postman и curl работают — у них нет CORS.
Простые запросы и preflight
Некоторые запросы браузер отправляет сразу: GET, HEAD или POST с «простыми» заголовками и типами вроде application/x-www-form-urlencoded.
Для остальных — например, PUT, DELETE, POST с Content-Type: application/json или с заголовком Authorization — браузер сначала шлёт preflight: запрос OPTIONS с вопросом, можно ли.
OPTIONS /orders HTTP/1.1
Origin: https://site.uz
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization
Сервер должен ответить успешным статусом и разрешающими заголовками. Только после этого уйдёт основной запрос.
Какие заголовки нужны
| Заголовок | Зачем |
|---|---|
Access-Control-Allow-Origin | Какой origin может читать ответ |
Access-Control-Allow-Methods | Какие методы разрешены (для preflight) |
Access-Control-Allow-Headers | Какие заголовки запроса разрешены |
Access-Control-Allow-Credentials | Можно ли передавать cookie |
Access-Control-Max-Age | Сколько кешировать ответ на preflight |
Vary: Origin | Чтобы кеш не отдал ответ для чужого origin |
Как исправить правильно
Настройте сервер на белый список origin. Пример для Express:
import express from "express";
import cors from "cors";
const app = express();
const allowed = ["https://site.uz", "https://admin.site.uz"];
app.use(cors({
origin: (origin, cb) => cb(null, !origin || allowed.includes(origin)),
credentials: true,
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"],
}));
Если CORS настраивается в nginx, убедитесь, что запрос OPTIONS получает ответ с заголовками, а не уходит в приложение, которое возвращает 404 или 401.
Cookie и credentials
Чтобы браузер отправил cookie на другой origin, нужно с двух сторон:
- на клиенте
fetch(url, { credentials: "include" }); - на сервере
Access-Control-Allow-Credentials: trueи конкретный origin вAccess-Control-Allow-Origin.
Значение * вместе с credentials браузер не примет. Кроме того, cookie для кросс-сайтовых запросов должны иметь SameSite=None; Secure.
Опасные «решения»
- Отражать любой Origin вместе с credentials. Тогда любой сайт сможет делать запросы от имени залогиненного пользователя.
*для приватного API. Допустимо только для публичных данных без авторизации.- Расширения браузера и флаги, отключающие CORS. Работает только у вас, пользователи всё равно увидят ошибку.
- Чужие публичные прокси. Ваши данные идут через сторонний сервер.
Хорошая альтернатива — один origin: отдавать фронтенд и API с одного домена через reverse proxy или dev-proxy сборщика. Тогда CORS не нужен вовсе.
Как диагностировать
- Откройте вкладку Network и найдите запрос
OPTIONS. - Проверьте его статус: он должен быть успешным, а не 404, 401 или 500.
- Сравните
Originзапроса со значениемAccess-Control-Allow-Origin— они должны совпадать посимвольно, включая протокол и порт. - Проверьте, не теряются ли заголовки при ошибке: многие серверы не добавляют CORS к ответам 4xx и 5xx, и реальная ошибка маскируется под CORS.