Что такое CORS и как он работает?

Ответ

CORS (Cross-Origin Resource Sharing, Совместное использование ресурсов между разными источниками) — это механизм, использующий дополнительные HTTP-заголовки, чтобы дать веб-приложению, работающему на одном домене (origin), право доступа к выбранным ресурсам с другого домена.

Браузеры из соображений безопасности блокируют межсайтовые HTTP-запросы, инициированные скриптами (политика Same-Origin). CORS — это стандарт, который позволяет серверу явно указать, какие внешние источники имеют право получать к нему доступ.

Как это работает на практике с Node.js/Express:

  1. Простой запрос (Simple Request): Для запросов GET/POST/HEAD с определёнными заголовками браузер сразу делает основной запрос, но проверяет заголовок Access-Control-Allow-Origin в ответе сервера.
  2. Предварительный запрос (Preflight Request): Для «непростых» запросов (например, с кастомными заголовками или методом PUT/DELETE) браузер сначала отправляет запрос OPTIONS (preflight) на тот же URL, чтобы проверить разрешённые методы и заголовки.

Базовая настройка CORS в Express с помощью middleware cors:

const express = require('express');
const cors = require('cors');
const app = express();

// Разрешить запросы с любого origin (для публичных API)
app.use(cors());

// Или настроить более строго
const corsOptions = {
  origin: 'https://my-trusted-frontend.com', // Разрешить только конкретный домен
  optionsSuccessStatus: 200, // Для старых браузеров
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
};
app.use(cors(corsOptions));

app.get('/api/data', (req, res) => {
  res.json({ message: 'Этот ответ виден только с https://my-trusted-frontend.com' });
});

Важные нюансы:

  • Заголовок Access-Control-Allow-Origin: * несовместим с использованием учётных данных (куки, HTTP-аутентификация). В этом случае необходимо указывать конкретный origin.
  • CORS — это защита в браузере. Сервер должен самостоятельно валидировать Origin для предотвращения CSRF-атак и прямых запросов извне браузера (например, через cURL или Postman).
  • Для обработки preflight-запросов OPTIONS middleware cors автоматически добавляет соответствующие заголовки.

Ответ 18+ 🔞

А, CORS! Ну это, блядь, классика, ядрёна вошь! Сидишь ты такой, пишешь фронтенд на одном домене, а твой бэкенд торчит на другом — и тут тебе браузер, как хитрая жопа, говорит: «Нет, дружок, не пущу!». Это и есть политика Same-Origin, ёпта.

А CORS — это такой стандартный способ сказать этому браузеру: «Да расслабься ты, чувак, это свои! Пусти!». По сути, сервер специальными заголовками в HTTP-ответе кричит: «Эй, браузер, вот этому фронту с адреса https://my-cool-app.com можно мои ресурсы таскать!».

Как эта хрень работает на практике, особенно с Node.js и Express?

Тут два сценария, и второй — просто пиздец какой замудрёный.

  1. Простой запрос (Simple Request). Это когда всё по-честному: GET, POST или HEAD, и заголовки стандартные. Браузер сразу шлёт основной запрос, но потом, получив ответ, смотрит в заголовок Access-Control-Allow-Origin. Если там звёздочка (*) или твой домен — пропускает. Если нет — тебе, дружок, в консоли красная ошибка, волнение ебать.

  2. Предварительный запрос (Preflight Request). А вот это уже ёперный театр! Если твой запрос «непростой» — скажем, ты метод PUT используешь или кастомный заголовок X-Api-Key прилепил — браузер, такой подозрительный, сначала шлёт запрос OPTIONS (это и есть preflight) на тот же самый адрес. Мол, «сервер, отзовись, что тебе можно?». И только получив в ответе разрешения на методы и заголовки, он пошнёт настоящий, основной запрос. Доверия ебать ноль, но безопасно.

Теперь, как это впендюрить в Express с помощью middleware cors (а без него — самим headers прописывать, терпения ноль ебать):

const express = require('express');
const cors = require('cors'); // Ставим: npm install cors
const app = express();

// Самый простой и опасный вариант — разрешить ВСЕМ (для публичных API сойдёт)
app.use(cors()); // Теперь любой фронт с любого домена может к тебе стучаться

// А вот так делают нормальные люди — настраивают строго
const corsOptions = {
  origin: 'https://my-very-trusted-frontend.com', // Только этому парню можно
  optionsSuccessStatus: 200, // Костыль для древних IE, которые с 200-м статусом для OPTIONS тупят
  methods: ['GET', 'POST', 'PUT', 'DELETE'], // Какие методы разрешены
  allowedHeaders: ['Content-Type', 'Authorization'] // Какие заголовки можно слать
};
app.use(cors(corsOptions)); // Применяем настройки

app.get('/api/data', (req, res) => {
  res.json({ message: 'Эти данные увидит только фронт с https://my-very-trusted-frontend.com' });
});

Важные нюансы, без которых сядешь в лужу:

  • Запомни раз и навсегда: заголовок Access-Control-Allow-Origin: * не дружит с учётными данными (куки, авторизация). Если ты в запросе credentials: 'include' указываешь, то сервер должен в ответе прописать конкретный origin, а не звёздочку. Иначе — облом.
  • CORS — это защита исключительно в браузере. Это не волшебный щит от всех атак. Любой пидарас шерстяной с помощью cURL или Postman может слать запросы напрямую к твоему API, и CORS ему — как кот сука собаке. Поэтому серверу всё равно надо самому проверять Origin и валидировать запросы, чтобы не ловить CSRF-атаки.
  • За preflight-запросы OPTIONS middleware cors заботится автоматически. Он сам добавит нужные заголовки в ответ. Без него пришлось бы вручную роут для OPTIONS писать — ещё та сосалка.

Видео-ответы