Для чего используется CORS?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Использование CORS (Cross-Origin Resource Sharing)
CORS (Cross-Origin Resource Sharing) — это механизм, который позволяет ограничивать и контролировать кросс-доменные запросы между браузером и сервером. CORS решает критическую проблему безопасности в веб-приложениях.
Проблема: Same-Origin Policy (SOP)
Все современные браузеры применяют Same-Origin Policy (SOP) — политику одного источника:
"Скрипт может получать данные только с того же источника (домена, протокола и порта)"
Основной источник: https://example.com:443
✅ Разрешено:
- https://example.com/api (тот же домен)
- https://example.com:443/data (тот же протокол и порт)
❌ Заблокировано:
- http://example.com/api (другой протокол)
- https://api.example.com (субдомен считается другим источником)
- https://example2.com (другой домен)
- https://example.com:8080 (другой порт)
Пример блокировки
// Загрузить страницу с https://myapp.com
// Попробать запрос с фронта
fetch('https://api.example.com/users')
.then(r => r.json())
.catch(e => console.log(e));
// Ошибка в консоли:
// Access to XMLHttpRequest at 'https://api.example.com/users'
// from origin 'https://myapp.com' has been blocked by CORS policy:
// No 'Access-Control-Allow-Origin' header is present on the requested resource.
Зачем нужен CORS
1. Безопасность
CORS защищает от атак:
<!-- Вредоносный сайт hack.com -->
<img src="https://bank.com/api/transfer?to=attacker&amount=1000">
<!-- Без CORS: браузер выполнит запрос от имени пользователя -->
<!-- С CORS: браузер заблокирует запрос, если нет правильного заголовка -->
2. Контроль доступа
Сервер может разрешить доступ только конкретным клиентам:
Сервер API (api.example.com):
- Может разрешить запросы с app.example.com
- Может разрешить запросы с mobile.example.com
- Но заблокировать с hack.com
3. Управление методами и заголовками
Сервер может разрешить только определённые HTTP методы и заголовки.
Как работает CORS
Простой запрос (Simple Request):
Для простых запросов (GET, POST, HEAD) с стандартными заголовками:
1. Браузер отправляет запрос с заголовком Origin
GET /api/users
Host: api.example.com
Origin: https://myapp.com
2. Сервер проверяет Origin и отвечает
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Content-Type: application/json
[{"id": 1, "name": "John"}]
3. Браузер получает ответ и позволяет скрипту его использовать
Preflight запрос (для сложных запросов):
Для сложных запросов (PUT, DELETE, PATCH) или кастомных заголовков браузер сначала отправляет OPTIONS запрос:
1. Браузер отправляет preflight (OPTIONS) запрос
OPTIONS /api/users
Host: api.example.com
Origin: https://myapp.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: content-type
2. Сервер отвечает, что это разрешено
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: content-type
Access-Control-Max-Age: 86400 (кэширование на 24 часа)
3. Браузер отправляет реальный запрос
PUT /api/users/1
...
4. Сервер отвечает нормально
CORS заголовки
На стороне сервера (ответ):
// Express пример
import cors from 'cors';
app.use(cors({
origin: 'https://myapp.com', // Разрешённый источник
methods: ['GET', 'POST', 'PUT'], // Разрешённые методы
allowedHeaders: ['Content-Type', 'Authorization'], // Разрешённые заголовки
credentials: true, // Разрешить cookies
maxAge: 86400 // Кэширование preflight на 24 часа
}));
// Или вручную
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'https://myapp.com');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Max-Age', '86400');
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
На стороне клиента (запрос):
// Fetch с credentials (cookies, auth headers)
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify({ name: 'John' }),
credentials: 'include' // Отправить cookies вместе с запросом
})
.then(r => r.json())
.catch(e => console.log('CORS ошибка:', e));
CORS стратегии
1. Разрешить все (небезопасно)
app.use(cors());
// или
res.setHeader('Access-Control-Allow-Origin', '*');
2. Разрешить конкретные домены
const allowedOrigins = [
'https://myapp.com',
'https://app.myapp.com',
'https://mobile.myapp.com'
];
app.use(cors({
origin: (origin, callback) => {
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
callback(new Error('CORS не разрешён'));
}
}
}));
3. Разрешить с паттернами
app.use(cors({
origin: /myapp\.com$/ // Разрешить все поддомены *.myapp.com
}));
Сценарии использования
Сценарий 1: SPA (Single Page Application)
Фронтенд: https://app.example.com
Бэкенд: https://api.example.com
Без CORS: фронтенд не может делать запросы к бэкенду
С CORS: бэкенд разрешает запросы с app.example.com
Сценарий 2: Мобильное приложение
Мобильный клиент НЕ использует CORS (это только для браузера)
Мобильное приложение может напрямую подключиться к API
Поэтому мобильные приложения часто используют другую авторизацию:
- API ключи
- OAuth токены
- JWT токены
Сценарий 3: Server-to-Server
Если бэкенд вызывает другой бэкенд, CORS НЕ применяется
CORS применяется только когда браузер делает запрос
Server A → Server B: работает без CORS
Браузер → Server A: CORS проверяется
Проблемы и решения
Проблема: credentials не отправляются
// ❌ Неправильно
fetch('https://api.example.com/users');
// ✅ Правильно
fetch('https://api.example.com/users', {
credentials: 'include' // Отправить cookies
});
// На сервере тоже нужно разрешить
res.setHeader('Access-Control-Allow-Credentials', 'true');
Проблема: кастомные заголовки блокируются
// Клиент отправляет:
fetch('https://api.example.com/users', {
headers: {
'X-Custom-Header': 'value'
}
});
// Сервер должен разрешить:
res.setHeader('Access-Control-Allow-Headers', 'X-Custom-Header, Content-Type');
Альтернативы CORS
1. JSONP (устаревший способ)
// Работал в старых браузерах, но не безопасный
<script src="https://api.example.com/users?callback=handleData"></script>
2. Proxy на бэкенде
Фронтенд → Свой бэкенд (/api/users) → Внешний API
Это обходит CORS, но добавляет latency
3. WebSocket
// WebSocket не подвергается CORS ограничениям
const ws = new WebSocket('wss://api.example.com/ws');
Вывод
CORS — это критический механизм безопасности:
- Same-Origin Policy — ограничивает кросс-доменные запросы
- CORS заголовки — сервер разрешает конкретные источники
- Preflight запрос — для сложных запросов браузер проверяет разрешение
- Правильная конфигурация — критична для безопасности и функциональности
Любой бэкенд-разработчик ДОЛЖЕН понимать и правильно конфигурировать CORS для защиты своего API.