Антон Ларичев

Введение
CORS (Cross-Origin Resource Sharing) — механизм безопасности, встроенный в каждый современный браузер. Он ограничивает HTTP-запросы между разными источниками. Источник (origin) — это комбинация протокола, домена и порта. Например, https://example.com:443 и https://api.example.com:443 — разные источники, даже если домен отличается только поддоменом.
Когда ваш фронтенд на http://localhost:3000 делает запрос к API на http://localhost:8000, браузер блокирует его по умолчанию. Именно это и называют ошибкой CORS. Разберём, как браузер принимает решение и как разрешить ситуацию на стороне сервера.
Как браузер проверяет CORS
Браузер делит запросы на два типа: простые и сложные (preflight).
Простые запросы
Запрос считается простым, если используется метод GET, POST или HEAD, а заголовок Content-Type имеет значение text/plain, multipart/form-data или application/x-www-form-urlencoded.
Для простых запросов браузер сразу отправляет запрос и проверяет заголовок Access-Control-Allow-Origin в ответе:
GET /api/users HTTP/1.1
Origin: http://localhost:3000
Host: api.example.com
Если сервер вернул правильный заголовок, браузер пропустит ответ в JavaScript-код:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json
Preflight-запросы
Если запрос не простой — например, метод DELETE или присутствует заголовок Authorization — браузер сначала отправляет preflight-запрос методом OPTIONS:
OPTIONS /api/users HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization, Content-Type
Сервер должен ответить с разрешёнными методами и заголовками:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400
Заголовок Access-Control-Max-Age указывает браузеру, сколько секунд кешировать результат preflight — это снижает количество лишних запросов.
Настройка CORS в Express.js
Самый простой способ настроить CORS в Node.js — использовать пакет cors.
npm install cors
Разрешить все источники
const express = require('express');
const cors = require('cors');
const app = express();
// Разрешаем все источники — только для локальной разработки
app.use(cors());
Такая настройка добавляет Access-Control-Allow-Origin: * ко всем ответам. В продакшне это небезопасно.
Настройка для конкретных источников
const corsOptions = {
// Список разрешённых источников
origin: ['https://example.com', 'https://www.example.com'],
// Разрешённые HTTP-методы
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
// Разрешённые заголовки в запросе
allowedHeaders: ['Content-Type', 'Authorization'],
// Разрешаем передачу куки и заголовка Authorization
credentials: true,
// Кешируем preflight на 24 часа
maxAge: 86400,
};
app.use(cors(corsOptions));
Динамическое разрешение источников
Если список источников хранится в базе данных или меняется в рантайме:
const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];
const corsOptions = {
origin: function (origin, callback) {
// Разрешаем запросы без origin (например, из Postman)
if (!origin) return callback(null, true);
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Источник не разрешён политикой CORS'));
}
},
credentials: true,
};
app.use(cors(corsOptions));
Настройка CORS в Next.js
В Next.js CORS удобнее всего настраивать через middleware — это позволяет применить политику сразу ко всем API-маршрутам.
Через middleware
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set('Access-Control-Allow-Origin', 'https://example.com');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Preflight-запрос должен вернуться немедленно, без бизнес-логики
if (request.method === 'OPTIONS') {
return new NextResponse(null, { status: 204, headers: response.headers });
}
return response;
}
export const config = {
matcher: '/api/:path*',
};
В отдельном API Route (App Router)
Если нужно задать CORS только для одного маршрута:
// app/api/users/route.ts
import { NextResponse } from 'next/server';
const corsHeaders = {
'Access-Control-Allow-Origin': 'https://example.com',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
// Обязательный обработчик preflight
export async function OPTIONS() {
return NextResponse.json({}, { headers: corsHeaders });
}
export async function GET() {
const users = [{ id: 1, name: 'Иван' }];
return NextResponse.json(users, { headers: corsHeaders });
}
Частые ошибки
Wildcard с credentials
Нельзя одновременно использовать Access-Control-Allow-Origin: * и Access-Control-Allow-Credentials: true — браузер явно запрещает такую комбинацию.
Неправильно:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Правильно:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
Не обрабатывается OPTIONS
Если сервер возвращает 404 или 405 на OPTIONS-запрос, preflight проваливается и браузер блокирует основной запрос. Убедитесь, что middleware или роутер обрабатывает OPTIONS для всех защищённых маршрутов.
Дублирование заголовков
Если CORS настроен и в middleware, и в обработчике маршрута, заголовок Access-Control-Allow-Origin дублируется. Браузер считает такой ответ некорректным и блокирует его. Настраивайте CORS в одном месте.
CORS не заменяет авторизацию
CORS — ограничение браузера, не защита сервера. curl и Postman игнорируют CORS. Всегда проверяйте токены и права доступа на стороне сервера, независимо от настроек CORS.
Заключение
CORS — это не баг и не случайность, а намеренный защитный механизм браузера. Понимание разницы между простыми и preflight-запросами позволяет быстро диагностировать ошибки и точно настраивать заголовки.
Для большинства проектов достаточно пакета cors в Express или middleware в Next.js с явным списком разрешённых источников. Избегайте wildcard * в продакшне, особенно если используете cookies или заголовок Authorization — это открывает дыры в безопасности, которые сложно отследить.






Комментарии
0