Акции и промокоды Отзывы о школах

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

#Блог

Представим ситуацию: мы разрабатываем современное веб-приложение, которое должно получать данные о погоде с внешнего API, загружать карты Google и интегрироваться с социальными сетями. Кажется, что ничего сложного — просто отправляем запросы на нужные адреса. Однако браузер категорически отказывается выполнять эти запросы, выбрасывая загадочную ошибку о 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. Этот заголовок информирует сервер о том, откуда именно пришёл запрос.

mekhanizm-cors

Диаграмма показывает путь запроса от клиента к серверу и обратно, где сервер с помощью заголовков сообщает браузеру, разрешён ли доступ. Это помогает быстро понять, кто принимает ключевые решения в процессе 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 список доменов. Браузер ожидает увидеть только одно значение: либо конкретный домен, либо *. Если он получит список, он проигнорирует заголовок и заблокирует запрос.

Правильное решение: динамическая проверка на сервере

Чтобы предоставить доступ нескольким доменам, сервер должен быть настроен на динамическую обработку запросов. Вот как выглядит правильный алгоритм:

  1. Получение Origin: Сервер принимает запрос от клиента и считывает значение заголовка Origin. В этом заголовке указан домен, с которого пришёл запрос.
  2. Проверка по «белому списку»: Сервер сравнивает полученный Origin с заранее определённым списком разрешённых доменов (так называемым «белым списком»).
  3. Динамический ответ: Если 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.

oshibki-cors

Столбчатая диаграмма демонстрирует условную частоту наиболее распространённых ошибок при работе с 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 обращаться ко всем необходимым сервисам.

mikroservisy-cors

Схема показывает, как фронтенд-приложение взаимодействует с несколькими сервисами (API, аутентификация, файловое хранилище) в разных доменах. Здесь CORS обеспечивает безопасный обмен данными между компонентами системы.

Возникает вопрос: можно ли обойтись без CORS в таких сценариях? Технически да — можно проксировать все запросы через основной сервер приложения. Однако это добавляет ненужную нагрузку и усложняет архитектуру. CORS предоставляет элегантное решение, позволяя компонентам взаимодействовать напрямую при соблюдении требований безопасности.

Заключение

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

  • CORS решает проблему политики одного источника. Он позволяет безопасно обращаться к ресурсам на других доменах.
  • Механизм работает через заголовки HTTP. Сервер указывает, кому разрешён доступ, а браузер принимает окончательное решение.
  • Preflight-запросы проверяют допустимость сложных операций. Это помогает избежать небезопасных действий.
  • Настройка CORS зависит от окружения. Для Node.js, Flask и Angular существуют готовые решения.
  • Ошибки CORS встречаются часто. Их можно устранить корректной настройкой заголовков и middleware.
  • Безопасность — главный приоритет. Использование * в продакшене недопустимо.
  • Практические кейсы показывают важность CORS. Он необходим для работы API, CDN и микросервисов.

Если вы только начинаете осваивать профессию фронтенд-разработчика, рекомендуем обратить внимание на подборку курсов по веб-разработке. В них есть как теоретическая часть для понимания базовых принципов, так и практические задания для закрепления навыков.

Читайте также
Категории курсов