Что такое CORS и как его правильно настроить

Представим ситуацию: мы разрабатываем современное веб-приложение, которое должно получать данные о погоде с внешнего API, загружать карты Google и интегрироваться с социальными сетями. Кажется, что ничего сложного — просто отправляем запросы на нужные адреса. Однако браузер категорически отказывается выполнять эти запросы, выбрасывая загадочную ошибку о CORS.
- Зачем нужен CORS
- Как работает механизм CORS
- Настройка CORS в разных окружениях
- Типичные ошибки и их решения
- Безопасность при использовании CORS
- Практические кейсы применения CORS
- Заключение
- Рекомендуем посмотреть курсы по веб разработке
Зачем нужен CORS
Cross-Origin Resource Sharing (CORS) — это механизм, который решает фундментальную проблему современной веб-разработки. Дело в том, что браузеры по умолчанию следуют политике «одного источника» (Same-Origin Policy), которая запрещает веб-страницам делать запросы к ресурсам, находящимся на других доменах, портах или протоколах. Эта политика была создана в целях безопасности — чтобы предотвратить ситуации, когда вредоносные скрипты могут получить доступ к конфиденциальным данным пользователя.
Однако современные приложения редко существуют в изоляции. Нам постоянно требуется обращаться к внешним сервисам: получать данные с API, загружать ресурсы с CDN, интегрироваться с платёжными системами. CORS предоставляет контролируемый способ обхода ограничений Same-Origin Policy, позволяя серверам явно указывать, какие домены могут получать доступ к их ресурсам.
Согласно нашим наблюдениям, понимание CORS становится критически важным навыком для любого фронтенд-разработчика. Без правильной настройки этого механизма невозможно создать полноценное современное веб-приложение, которое взаимодействует с внешним миром.
Как работает механизм CORS
Давайте разберёмся, как именно браузер принимает решение о том, разрешить или запретить кросс-доменный запрос. Весь процесс можно разбить на несколько ключевых этапов, каждый из которых играет важную роль в обеспечении безопасности.
Шаг 1: Отправка запроса с заголовком Origin
Когда наше приложение на домене https://frontend.example.com пытается обратиться к API на https://api.example.com, браузер автоматически добавляет к запросу специальный заголовок Origin: https://frontend.example.com. Этот заголовок информирует сервер о том, откуда именно пришёл запрос.

Диаграмма показывает путь запроса от клиента к серверу и обратно, где сервер с помощью заголовков сообщает браузеру, разрешён ли доступ. Это помогает быстро понять, кто принимает ключевые решения в процессе CORS.
Шаг 2: Анализ ответа сервера
Сервер, получив запрос, должен решить: разрешить ли доступ этому домену? Если да, то в ответе он включает заголовок Access-Control-Allow-Origin, указывающий разрешённые домены. Например: Access-Control-Allow-Origin: https://frontend.example.com или Access-Control-Allow-Origin: * для разрешения всем доменам.
Шаг 3: Финальное решение браузера
Браузер сравнивает значение Origin с полученным Access-Control-Allow-Origin. Если они совпадают (или сервер разрешил доступ всем доменам через *), запрос выполняется успешно. В противном случае браузер блокирует получение данных и выбрасывает знакомую многим ошибку CORS.
Ключевые заголовки CORS
Механизм CORS оперирует несколькими важными HTTP-заголовками:
- Access-Control-Allow-Methods — перечисляет HTTP-методы (GET, POST, PUT, DELETE), которые разрешены для кросс-доменных запросов.
- Access-Control-Allow-Headers — указывает, какие заголовки клиент может включать в запрос помимо стандартных.
Конечно, вот фрагмент, который можно скопировать и вставить прямо в статью для исправления неточности.
Важное уточнение: как правильно указывать разрешённые домены
При настройке заголовка Access-Control-Allow-Origin разработчики часто допускают одну и ту же ошибку, полагая, что он может принимать несколько значений. Давайте разберёмся, как это работает на самом деле.
Распространённое заблуждение: список доменов
Может показаться логичным, что для разрешения доступа нескольким сайтам можно просто перечислить их через запятую, например:
# ЭТО НЕПРАВИЛЬНО И НЕ БУДЕТ РАБОТАТЬ!
Access-Control-Allow-Origin: https://frontend.example.com, https://app.example.com
Это неверно. Спецификация CORS не позволяет передавать в заголовке Access-Control-Allow-Origin список доменов. Браузер ожидает увидеть только одно значение: либо конкретный домен, либо *. Если он получит список, он проигнорирует заголовок и заблокирует запрос.
Правильное решение: динамическая проверка на сервере
Чтобы предоставить доступ нескольким доменам, сервер должен быть настроен на динамическую обработку запросов. Вот как выглядит правильный алгоритм:
- Получение Origin: Сервер принимает запрос от клиента и считывает значение заголовка Origin. В этом заголовке указан домен, с которого пришёл запрос.
- Проверка по «белому списку»: Сервер сравнивает полученный Origin с заранее определённым списком разрешённых доменов (так называемым «белым списком»).
- Динамический ответ: Если Origin запроса находится в этом списке, сервер копирует его значение и вставляет в заголовок ответа Access-Control-Allow-Origin.
Таким образом, для каждого разрешённого запроса ответ будет уникальным, но всегда валидным:
- Запрос с Origin: https://frontend.example.com получит ответ Access-Control-Allow-Origin: https://frontend.example.com.
- Запрос с Origin: https://app.example.com получит ответ Access-Control-Allow-Origin: https://app.example.com.
- Запрос с Origin: https://malicious-site.com не найдёт совпадения в списке, и сервер либо вообще не вернёт этот заголовок, либо вернёт домен по умолчанию, что приведёт к блокировке запроса браузером.
Этот подход обеспечивает безопасность и соответствует стандартам CORS.
Особый случай: preflight-запросы
Не все запросы выполняются напрямую. Если мы отправляем «сложный» запрос (например, POST с нестандартными заголовками или PUT-запрос), браузер сначала делает предварительный OPTIONS-запрос к серверу — так называемый preflight. Этот запрос проверяет, разрешён ли основной запрос, и только после получения положительного ответа выполняет реальный запрос с данными. Понимание этого механизма критически важно для правильной настройки сервера.
Настройка CORS в разных окружениях
Теория — это хорошо, но как применить знания на практике? Рассмотрим конкретные способы настройки CORS в популярных серверных и клиентских окружениях, с которыми мы регулярно сталкиваемся в реальных проектах.
Настройка CORS на сервере
Node.js с Express: простой подход
Самый быстрый способ включить CORS в Express-приложении — использовать готовую библиотеку cors. Установим её и настроим базовую конфигурацию:
const express = require('express');
const cors = require('cors');
const app = express();
// Разрешаем CORS для всех доменов (подходит для разработки)
app.use(cors());
// Или более безопасный вариант для продакшена
app.use(cors({
origin: ['https://frontend.example.com', 'https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
app.listen(3000, () => console.log('Server running on port 3000'));
Ручная настройка в Node.js
Если мы предпочитаем больший контроль или не хотим подключать дополнительные зависимости, можем настроить CORS самостоятельно:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://frontend.example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Обработка preflight-запросов
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
Python с Flask
В экосистеме Python процесс ещё проще благодаря библиотеке flask-cors:
from flask import Flask from flask_cors import CORS app = Flask(__name__) CORS(app) # Включаем CORS для всех маршрутов # Или с детальной настройкой CORS(app, origins=['https://frontend.example.com'], methods=['GET', 'POST'], allow_headers=['Content-Type', 'Authorization']) if __name__ == '__main__': app.run()
Важная деталь: при настройке origin в продакшене всегда указывайте конкретные домены вместо символа *. Это существенно повышает безопасность приложения и защищает от потенциальных атак.
Настройка CORS на клиенте
Иногда проблема CORS возникает только в процессе разработки, когда наш фронтенд работает на localhost:4200, а API — на localhost:3000. В продакшене эти сервисы могут находиться на одном домене, но для локальной разработки нужно решение.
Angular: настройка прокси
В Angular мы можем создать файл proxy.conf.json в корне проекта:
{
"/api/*": {
"target": "http://localhost:3000",
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
}
}
Затем запускаем dev-сервер с использованием прокси:
ng serve --proxy-config proxy.conf.json
Теперь все запросы к /api/* будут автоматически перенаправляться на наш бэкенд, обходя ограничения CORS. Браузер будет думать, что запросы идут на тот же домен, что и фронтенд-приложение.
Этот подход особенно удобен для команд, где фронтенд и бэкенд разрабатываются параллельно разными специалистами.
Типичные ошибки и их решения
В работе с CORS мы регулярно сталкиваемся с одними и теми же проблемами. Давайте разберём самые частые ошибки и эффективные способы их устранения — это сэкономит нам много времени на отладке.
No ‘Access-Control-Allow-Origin’ header is present on the requested resource
Классическая ошибка, с которой знаком каждый фронтенд-разработчик. Возникает, когда сервер не отправляет нужный заголовок CORS.
Решение: Убедитесь, что на сервере настроена отправка заголовка Access-Control-Allow-Origin. Проверьте, что middleware или обработчик CORS применяется ко всем нужным маршрутам.
// Неправильно: middleware добавлен после маршрутов
app.get('/api/data', (req, res) => res.json({data: 'test'}));
app.use(cors());
// Правильно: middleware добавлен до маршрутов
app.use(cors());
app.get('/api/data', (req, res) => res.json({data: 'test'}));
Preflight OPTIONS request fails
Браузер отправляет предварительный OPTIONS-запрос, но сервер не умеет его обрабатывать или возвращает ошибку.
Решение: Добавьте обработку OPTIONS-запросов на сервере:
app.use((req, res, next) => {
if (req.method === 'OPTIONS') {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return res.sendStatus(200);
}
next();
});
CORS error with cookies or authentication
При работе с cookies или токенами аутентификации стандартная настройка CORS может не сработать.
Решение: Для передачи cookies нужна дополнительная настройка:
// На сервере
app.use(cors({
origin: 'https://frontend.example.com', // Не может быть '*' при credentials: true
credentials: true
}));
// На клиенте (fetch)
fetch('https://api.example.com/data', {
credentials: 'include'
});
Wildcard ‘*’ not allowed when credentials are enabled
Попытка использовать Access-Control-Allow-Origin: * вместе с credentials: true.
Решение: При включении credentials необходимо явно указать допустимые домены:
// Неправильно
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true');
// Правильно
res.header('Access-Control-Allow-Origin', 'https://frontend.example.com');
res.header('Access-Control-Allow-Credentials', 'true');
Custom headers blocked
Браузер блокирует запросы с нестандартными заголовками (например, X-API-Key).
Решение: Добавьте нужные заголовки в Access-Control-Allow-Headers:
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key, X-Requested-With');
Наш опыт показывает, что большинство проблем с CORS решается правильной настройкой этих базовых аспектов. Главное — помнить о последовательности обработки middleware и особенностях работы с cookies.

Столбчатая диаграмма демонстрирует условную частоту наиболее распространённых ошибок при работе с CORS. Такой обзор помогает сразу понять, какие проблемы встречаются чаще всего и требуют особого внимания.
Безопасность при использовании CORS
CORS представляет собой компромисс между функциональностью и безопасностью. С одной стороны, он позволяет создавать гибкие современные приложения, с другой — неправильная настройка может открыть серьёзные уязвимости. Давайте разберёмся, как найти правильный баланс.
Опасность использования wildcard (*)
Самая распространённая ошибка — установка Access-Control-Allow-Origin: * в продакшене. Кажется, что это решает все проблемы одним махом, но на самом деле открывает наши API для любого сайта в интернете. Представьте: злоумышленник может создать вредоносную страницу, которая будет делать запросы к нашему API от имени ничего не подозревающих пользователей.
Принцип наименьших привилегий
Правильный подход — явно указывать только те домены, которым действительно нужен доступ:
// Небезопасно для продакшена
app.use(cors({
origin: '*'
}));
// Безопасная настройка
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
methods: ['GET', 'POST'], // Только необходимые методы
allowedHeaders: ['Content-Type', 'Authorization'] // Только нужные заголовки
}));
Влияние на защиту от CSRF и XSS
CORS не является панацеей от всех атак. Неправильная настройка может усугубить уязвимости к межсайтовой подделке запросов (CSRF) и межсайтовому скриптингу (XSS). Если мы разрешаем доступ слишком широкому кругу доменов, злоумышленник может использовать это для проведения атак с других сайтов.
Исследования показывают, что сочетание правильно настроенного CORS с дополнительными мерами безопасности (CSRF-токены, Content Security Policy, HTTPS) обеспечивает надёжную защиту современных веб-приложений.
Баланс между удобством и безопасностью
В процессе разработки мы часто сталкиваемся с искушением упростить настройки CORS ради скорости. Однако стоит помнить: то, что работает в dev-окружении, не всегда подходит для продакшена. Рекомендуем использовать переменные окружения для различных конфигураций и никогда не деплоить приложение с wildcard-настройками в production.
Практические кейсы применения CORS
Теория становится понятнее, когда мы видим реальные примеры использования. Рассмотрим несколько типичных сценариев, где CORS играет ключевую роль в функционировании современных веб-приложений.
Работа с изображениями и файлами с CDN
Часто мы размещаем статические ресурсы на отдельном домене или CDN для оптимизации производительности. Например, наше приложение находится на app.example.com, а изображения — на cdn.example.com. Для обработки изображений через Canvas API браузеру нужно разрешение CORS:
// Без правильной настройки CORS этот код вызовет ошибку
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = 'https://cdn.example.com/photo.jpg';
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
};
Интеграция с публичными API
Современные приложения активно используют внешние сервисы. Интеграция с картами Google, API социальных сетей, сервисами погоды — все эти задачи требуют корректной настройки CORS со стороны поставщика API. Например, для работы с API GitHub нам не нужно беспокоиться о настройке CORS — GitHub уже настроил свои серверы для поддержки кросс-доменных запросов.
Микросервисная архитектура
В современных корпоративных приложениях различные части системы часто развёрнуты на разных поддоменах: auth.company.com для аутентификации, api.company.com для бизнес-логики, files.company.com для файлового хранилища. CORS здесь обеспечивает безопасное взаимодействие между компонентами системы, позволяя фронтенду на app.company.com обращаться ко всем необходимым сервисам.

Схема показывает, как фронтенд-приложение взаимодействует с несколькими сервисами (API, аутентификация, файловое хранилище) в разных доменах. Здесь CORS обеспечивает безопасный обмен данными между компонентами системы.
Возникает вопрос: можно ли обойтись без CORS в таких сценариях? Технически да — можно проксировать все запросы через основной сервер приложения. Однако это добавляет ненужную нагрузку и усложняет архитектуру. CORS предоставляет элегантное решение, позволяя компонентам взаимодействовать напрямую при соблюдении требований безопасности.
Заключение
Cross-Origin Resource Sharing — это не просто техническая деталь, а фундаментальный механизм, который делает возможным существование современного интернета в том виде, в каком мы его знаем. Мы рассмотрели, как CORS решает противоречие между безопасностью браузера и потребностями разработчиков в создании интегрированных приложений.
- CORS решает проблему политики одного источника. Он позволяет безопасно обращаться к ресурсам на других доменах.
- Механизм работает через заголовки HTTP. Сервер указывает, кому разрешён доступ, а браузер принимает окончательное решение.
- Preflight-запросы проверяют допустимость сложных операций. Это помогает избежать небезопасных действий.
- Настройка CORS зависит от окружения. Для Node.js, Flask и Angular существуют готовые решения.
- Ошибки CORS встречаются часто. Их можно устранить корректной настройкой заголовков и middleware.
- Безопасность — главный приоритет. Использование * в продакшене недопустимо.
- Практические кейсы показывают важность CORS. Он необходим для работы API, CDN и микросервисов.
Если вы только начинаете осваивать профессию фронтенд-разработчика, рекомендуем обратить внимание на подборку курсов по веб-разработке. В них есть как теоретическая часть для понимания базовых принципов, так и практические задания для закрепления навыков.
Рекомендуем посмотреть курсы по веб разработке
| Курс | Школа | Цена | Рассрочка | Длительность | Дата начала | Ссылка на курс |
|---|---|---|---|---|---|---|
|
Веб-разработчик
|
Eduson Academy
76 отзывов
|
Цена
Ещё -5% по промокоду
119 000 ₽
|
От
9 917 ₽/мес
|
Длительность
11.1 месяцев
|
Старт
6 февраля
|
Ссылка на курс |
|
Веб-разработчик с нуля до PRO
|
Skillbox
191 отзыв
|
Цена
Ещё -20% по промокоду
294 783 ₽
589 565 ₽
|
От
8 670 ₽/мес
Без переплат на 1 год.
|
Длительность
10 месяцев
|
Старт
скоро
|
Ссылка на курс |
|
Веб-разработчик с нуля
|
Нетология
44 отзыва
|
Цена
с промокодом kursy-online
140 300 ₽
283 430 ₽
|
От
4 330 ₽/мес
Без переплат на 2 года.
7 222 ₽/мес
|
Длительность
17 месяцев
|
Старт
5 января
|
Ссылка на курс |
|
Fullstack-разработчик на python (с нуля)
|
Eduson Academy
76 отзывов
|
Цена
Ещё -17% по промокоду
147 000 ₽
495 408 ₽
|
От
12 250 ₽/мес
20 642 ₽/мес
|
Длительность
7 месяцев
|
Старт
16 декабря
|
Ссылка на курс |
|
Профессия Веб-разработчик
|
Skillbox
191 отзыв
|
Цена
Ещё -20% по промокоду
145 274 ₽
290 548 ₽
|
От
4 273 ₽/мес
Без переплат на 34 месяца с отсрочкой платежа 3 месяца.
|
Длительность
24 месяца
|
Старт
9 декабря
|
Ссылка на курс |
Съёмка в движении — как правильно снимать движущиеся объекты без потери качества
Съемка в движении — это искусство, требующее правильных настроек камеры и техники. Узнайте, как заморозить момент или передать динамику без потери качества.
Вложенные классы в Python: что это, как использовать и когда они нужны
Что делает вложенные классы удобными, а что — потенциально опасными? Разбираем синтаксис, область применения и альтернативы в современных проектах на Python.
Инструменты для автоматизации тестирования: что выбрать и почему?
Автоматизация тестирования — неотъемлемая часть современного IT. Разберемся, какие инструменты подойдут для ваших задач, как их настроить и использовать эффективно.
Лучшие библиотеки Python для Data Science
Если вы ищете удобные библиотеки для data science python и хотите понять, какие инструменты действительно нужны на старте, этот материал поможет разобраться. Какие решения ускоряют обработку данных? Какие библиотеки подойдут для визуализации, статистики и машинного обучения? Разбираем всё простым языком и даём ориентиры для новичков.