Что такое CORS и как его решать во фронтенде?
Что такое CORS
CORS (Cross-Origin Resource Sharing) — это механизм безопасности браузера, реализующий политику одного источника (Same-Origin Policy). Браузер блокирует запросы из JavaScript к ресурсам на другом origins, если сервер явно не разрешил это через специальные HTTP-заголовки.
Origin состоит из трёх компонентов: протокол, домен и порт. Если хотя бы один отличается — это cross-origin запрос.
https://app.com → https://api.app.com // разные домены — CORS
https://app.com → http://app.com // разные протоколы — CORS
https://app.com:3000 → https://app.com // разные порты — CORS
Как работает CORS
Простые запросы (Simple Requests)
Для GET/POST запросов с ограниченными заголовками браузер сразу отправляет запрос, но проверяет ответный заголовок Access-Control-Allow-Origin.
Preflight-запросы
Для «сложных» запросов (PUT, DELETE, кастомные заголовки, JSON-тело) браузер сначала отправляет OPTIONS-запрос — preflight. Сервер должен ответить разрешающими заголовками, иначе основной запрос не уйдёт.
Решения
1. Настройка заголовков на сервере (правильный путь)
Сервер должен вернуть заголовки:
Access-Control-Allow-Origin: https://app.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
2. Прокси на уровне dev-сервера (для разработки)
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
// Все запросы /api/* проксируются на бэкенд
destination: 'http://localhost:4000/:path*',
},
];
},
};
Или в Vite:
// vite.config.ts
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:4000',
changeOrigin: true,
// Убираем префикс /api при проксировании
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
};
3. Credentials и куки
Если нужно передавать cookies или Authorization, на фронтенде обязательно:
// fetch — указываем include для передачи куки
fetch('https://api.example.com/data', {
credentials: 'include',
});
// axios — аналогично
axios.get('https://api.example.com/data', {
withCredentials: true,
});
При этом сервер не может вернуть Access-Control-Allow-Origin: * — только конкретный origin.
4. CORS в production
В продакшене прокси настраивается на уровне nginx или API-gateway:
add_header Access-Control-Allow-Origin "https://app.com";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
Важные нюансы
- CORS — это браузерный механизм. curl и Postman его не соблюдают.
Access-Control-Allow-Origin: *несовместим сcredentials: 'include'.- Preflight кэшируется через
Access-Control-Max-Age. - Ошибка CORS в консоли означает проблему на сервере, а не на фронтенде.
Что хочет услышать интервьюер
Кандидат понимает, что CORS — это браузерная политика безопасности, а не серверная ошибка
Знает разницу между simple request и preflight (OPTIONS)
Понимает, что решение CORS — это настройка заголовков на сервере, а не на фронтенде
Умеет настраивать прокси в dev-окружении (Vite, Webpack, Next.js)
Знает про credentials и почему Access-Control-Allow-Origin: * несовместим с куками
Пример: Preflight и credentials с fetch
// Запрос с кастомным заголовком вызовет preflight
async function fetchUserData(token: string) {
const response = await fetch('https://api.example.com/user', {
method: 'GET',
// Этот заголовок делает запрос «сложным» — браузер пошлёт preflight
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
// Передаём куки и credentials вместе с запросом
credentials: 'include',
});
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}
Пример: Прокси в Vite для обхода CORS при разработке
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
server: {
proxy: {
// Все запросы на /api/* уходят на бэкенд
'/api': {
target: 'http://localhost:4000',
changeOrigin: true,
// Убираем /api из пути: /api/users → /users
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
});
Типичные ошибки
Думают, что CORS можно «починить» только на фронтенде без изменений на сервере
Путают preflight-запрос с основным запросом и не понимают, зачем браузер шлёт OPTIONS
Используют Access-Control-Allow-Origin: * в связке с credentials: 'include' и удивляются ошибке
Пытаются отключить CORS в браузере (флаги) как «решение» в продакшене
Не знают разницы между режимом разработки (прокси) и продакшен-конфигурацией


