🚧 CORS
💡 Основная идея
CORS — это механизм безопасности на стороне браузера, а не сервера — он решает, разрешено ли фронтенд JS читать кросс-доменный ответ, а не может ли сам запрос быть отправлен. Это невозможно исправить со стороны клиента, если сервер не отправляет нужные заголовки.
🌐 Origin
Origin — это тройка scheme + host + port. Два URL считаются одного происхождения только если совпадают все три компонента — http:// и https://, или :3000 и :8080 на одном и том же хосте, считаются разными origin.
🔀 Простые и preflight-запросы
- Простые запросы (GET/HEAD/POST только с небольшим набором стандартных заголовков и типов контента) идут прямо на сервер. Ответ приходит обратно, и браузер решает постфактум, разрешено ли JS его читать, основываясь на
Access-Control-Allow-Origin. - Preflight-запросы — всё, что использует не-простой метод (PUT, DELETE, PATCH) или кастомные заголовки (
Authorization,Content-Type: application/jsonи т.д.) — сначала запускаютOPTIONS-запрос, спрашивающий у сервера, что разрешено, до отправки реального запроса. Только если preflight-ответ сервера разрешает метод/заголовки, браузер отправляет фактический запрос.
⚙️ Настройка на стороне сервера
- Явно разрешайте нужный
Origin, методы и заголовки — не полагайтесь на значения по умолчанию. - Никогда не сочетайте
Access-Control-Allow-Origin: *сAccess-Control-Allow-Credentials: true— спецификация это запрещает, и браузеры отклоняют такое сочетание. - Для API-прокси (например, Nuxt/Node backend-for-frontend), маршрутизация запросов через собственный сервер полностью убирает кросс-доменный переход между браузером и бэкендом — заголовки CORS не нужны.
app.use(
cors({
origin: ["https://app.example.com"],
credentials: true,
allowedHeaders: ["content-type", "authorization"],
methods: ["GET", "POST", "PATCH", "DELETE"],
}),
)🐞 Отладка
- Ошибки CORS невозможно исправить на клиенте, если сервер не отправляет нужные заголовки — обходного пути на стороне клиента не существует (кроме использования прокси).
- Проверьте вкладку Network на предмет ответа preflight-запроса
OPTIONSи «blocked reason» браузера. - Если куки не отправляются кросс-доменно, проверьте
Access-Control-Allow-Credentialsна сервере иcredentials: 'include'на клиенте — а также чтоAccess-Control-Allow-Originуказывает точный origin, а не*.
HTTP & Network Cookies & Storage — SameSite и кросс-доменные куки browser