Настройка CORS: Access-Control-Allow-Origin для межсайтовых запросов
Для кого эта статья:
- Веб-разработчики, включая как начинающих, так и опытных
- Специалисты по безопасности веб-приложений
Студенты и единомышленники, изучающие курс веб-разработки
Ошибка "No 'Access-Control-Allow-Origin' header is present" способна выбить почву из-под ног даже у опытных веб-разработчиков. Каждый, кто интегрировал фронтенд с API или пытался загрузить ресурсы с другого домена, встречал эту фатальную красную строку в консоли браузера. CORS-политики — это не просто технический барьер, а защитный механизм современного веб-пространства, без понимания которого невозможно создавать безопасные и функциональные веб-приложения. Разберемся, как работает Access-Control-Allow-Origin, почему он критически важен, и как правильно настроить его для различных сценариев. 🔒
Если вы постоянно сталкиваетесь с ограничениями CORS и хотите понять, как профессионально обходить эти барьеры, обучение веб-разработке от Skypro — ваш надежный путь к мастерству. На курсе вы не только освоите настройку межсайтовых взаимодействий, но и получите целостное понимание веб-архитектуры от экспертов-практиков, работающих с реальными проектами. Вместо борьбы с ошибками вы научитесь предвидеть их и создавать элегантные решения.
Что такое CORS: защита и роль Access-Control-Allow-Origin
Cross-Origin Resource Sharing (CORS) — это механизм безопасности, реализованный в современных браузерах для контроля доступа к ресурсам, расположенным за пределами исходного домена. Фактически, это реализация принципа Same-Origin Policy с возможностью гибкого управления исключениями.
Когда JavaScript в браузере пытается выполнить запрос к серверу, находящемуся на другом домене, порту или протоколе, браузер сначала проверяет, разрешает ли целевой сервер такие запросы. Вот здесь и вступает в игру заголовок Access-Control-Allow-Origin.
Алексей, ведущий DevOps-инженер: Однажды я столкнулся с интересной ситуацией при миграции монолитного приложения на микросервисную архитектуру. Наш фронтенд был размещен на домене example.com, а новый API — на api.example.com. Казалось бы, незначительное различие, но вдруг все запросы с фронтенда начали блокироваться браузерами. Проблема выявилась быстро: отсутствие корректных CORS-заголовков. Мы добавили Access-Control-Allow-Origin: https://example.com на API-сервер, но столкнулись с новой проблемой при тестировании на локальных машинах разработчиков, где фронтенд работал на localhost:3000. Решение было элегантным: мы настроили динамическое формирование заголовка на основе Origin запроса, но с валидацией против списка разрешенных доменов. Это позволило гибко поддерживать разные окружения без ущерба для безопасности. Важный урок: никогда не используйте Access-Control-Allow-Origin: * в продакшене для API, содержащих чувствительные данные.
Без правильной настройки CORS-политики, даже самый совершенный API остается недоступным для клиентских приложений, работающих в других доменах. Представьте, что у вас есть безупречно работающий сервис на backend.example.com, но ваш фронтенд на frontend.example.com не может получить к нему доступ — классический случай блокировки из-за Same-Origin Policy.
| Компонент | Функция в CORS-механизме |
|---|---|
| Браузер | Применяет ограничения, отправляет предварительные запросы |
| Сервер | Предоставляет разрешения через заголовки |
| Access-Control-Allow-Origin | Указывает, каким доменам разрешено взаимодействие |
| Origin (заголовок запроса) | Сообщает серверу, откуда пришел запрос |
Важно понимать, что CORS — это не замена для других механизмов безопасности. Это дополнительный уровень защиты, предотвращающий несанкционированное использование вашего API из непредусмотренных источников.

Механизм работы заголовка Access-Control-Allow-Origin
Процесс кросс-доменного запроса с задействованием Access-Control-Allow-Origin можно разделить на несколько ключевых этапов:
- Инициация запроса: JavaScript-код на странице пытается выполнить запрос к ресурсу на другом домене.
- Добавление Origin: Браузер автоматически добавляет заголовок Origin к запросу, указывающий домен, с которого выполняется запрос.
- Проверка сервером: Сервер проверяет заголовок Origin и решает, разрешить или запретить доступ.
- Ответ сервера: Сервер включает в ответ заголовок Access-Control-Allow-Origin, определяющий, каким доменам разрешен доступ.
- Валидация браузером: Браузер проверяет, соответствует ли Origin значению в Access-Control-Allow-Origin.
- Выполнение или блокировка: На основе результата проверки браузер либо предоставляет JavaScript доступ к ответу, либо блокирует его, генерируя CORS-ошибку.
Для сложных запросов (например, с нестандартными заголовками или методами, отличными от GET/POST) браузер сначала выполняет предварительный запрос OPTIONS (preflight request). Это дополнительная проверка, которая позволяет серверу более детально контролировать кросс-доменные взаимодействия. 🔍
Следует отметить, что CORS-ограничения применяются только браузерами. Запросы, выполняемые серверным кодом или инструментами вроде curl, не подчиняются этим правилам. Это делает CORS механизмом защиты именно пользовательских данных в контексте браузера.
Важный нюанс: блокировка происходит на стороне браузера после получения ответа от сервера. Запрос фактически выполняется, и сервер обрабатывает его, но браузер не разрешает JavaScript-коду доступ к полученным данным, если CORS-заголовки не соответствуют требованиям.
Дмитрий, руководитель отдела фронтенд-разработки: В нашем проекте мы столкнулись с загадочной проблемой. Пользователи сообщали, что после авторизации их сессия иногда "забывалась", и им приходилось повторно входить в систему. Изучение логов показало, что авторизация проходила успешно, но запросы к API иногда блокировались из-за CORS. После детального анализа выяснилось, что наше SPA-приложение в некоторых случаях выполняло запросы с "пустым" Origin (происходило это из-за особенностей маршрутизации и перезагрузки страницы). Мы решили проблему двумя способами: улучшили логику клиентской маршрутизации, чтобы избежать полных перезагрузок, и настроили сервер для обработки запросов с пустым Origin. Но главный вывод: недостаточно просто добавить Access-Control-Allow-Origin с нужными доменами — необходимо предусмотреть все сценарии взаимодействия пользователя с приложением.
Синтаксис и допустимые значения для кросс-доменных запросов
Заголовок Access-Control-Allow-Origin может принимать ограниченный набор значений, и неправильное использование этих значений — частая причина проблем с CORS. Рассмотрим возможные варианты и их практическое применение.
| Значение | Описание | Примеры использования | Уровень безопасности |
|---|---|---|---|
| * | Разрешает доступ с любого домена | Публичные API, CDN для статических ресурсов | Низкий |
| Конкретный домен | Разрешает доступ только с указанного домена | Интеграция между конкретными сервисами | Высокий |
| null | Специальное значение для локальных файлов | Локальная разработка, file:// протокол | Средний |
| Динамическое значение | Значение формируется на основе Origin запроса | Мультидоменные системы с валидацией | Зависит от реализации |
Важно понимать, что Access-Control-Allow-Origin принимает только одно значение, за исключением универсального "*". Это означает, что для поддержки нескольких доменов необходимо динамически формировать этот заголовок на сервере на основе входящего заголовка Origin.
Пример корректного использования:
// Проверка входящего Origin
const allowedOrigins = ['https://example.com', 'https://sub.example.com'];
const origin = request.headers.origin;
if (allowedOrigins.includes(origin)) {
response.setHeader('Access-Control-Allow-Origin', origin);
}
Ошибки, которых следует избегать при настройке заголовка:
- Использование нескольких доменов через запятую — это недопустимый синтаксис, который приведет к игнорированию заголовка.
- Использование значения "*", когда запрос включает учетные данные (credentials) — браузеры блокируют такие комбинации для защиты конфиденциальных данных.
- Использование протокола без домена (например, "https://") — заголовок должен содержать полный URL или "*".
- Использование подстановочных знаков для поддоменов (например, "*.example.com") — это не поддерживается спецификацией.
При работе с учетными данными (cookies, HTTP-аутентификацией) необходимо также установить заголовок Access-Control-Allow-Credentials: true и указать конкретный домен в Access-Control-Allow-Origin. ⚠️
Настройка CORS-политики на различных серверах
Корректная настройка CORS-политики зависит от используемого сервера или фреймворка. Рассмотрим наиболее распространенные варианты и их особенности.
Nginx
Nginx — один из самых популярных веб-серверов, часто используемый для балансировки нагрузки и обратного прокси. Вот как настроить CORS в его конфигурации:
server {
listen 80;
server_name api.example.com;
location / {
# Основные CORS-заголовки
add_header 'Access-Control-Allow-Origin' 'https://example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
# Обработка предварительных запросов OPTIONS
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# Проксирование запросов к бэкенду
proxy_pass http://backend;
}
}
Apache
Для Apache настройка CORS обычно выполняется через модуль mod_headers:
<IfModule mod_headers.c>
<Directory /path/to/api>
SetEnvIf Origin "^https://(www\.)?(example\.com)$" ALLOWED_ORIGIN=$0
Header set Access-Control-Allow-Origin %{ALLOWED_ORIGIN}e env=ALLOWED_ORIGIN
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE"
Header set Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"
# Обработка OPTIONS
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
</Directory>
</IfModule>
Node.js (Express)
В Express.js можно использовать middleware cors или реализовать собственную логику:
const express = require('express');
const app = express();
// Вариант 1: Использование middleware cors
const cors = require('cors');
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// Вариант 2: Собственная реализация
app.use((req, res, next) => {
const allowedOrigins = ['https://example.com', 'https://www.example.com'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.status(204).end();
}
next();
});
Python (Django)
Для Django существует пакет django-cors-headers:
# settings.py
INSTALLED_APPS = [
# ...
'corsheaders',
# ...
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# Должен быть размещен перед CommonMiddleware
'django.middleware.common.CommonMiddleware',
# ...
]
# Разрешить конкретные домены
CORS_ALLOWED_ORIGINS = [
"https://example.com",
"https://sub.example.com",
]
# Или разрешить все домены (не рекомендуется для продакшена)
# CORS_ALLOW_ALL_ORIGINS = True
Выбор способа настройки CORS зависит от вашей инфраструктуры. В микросервисной архитектуре часто лучше настраивать CORS на уровне API-шлюза или обратного прокси, чтобы обеспечить консистентную политику для всех сервисов. 🔧
Решение типичных проблем с Access-Control-Allow-Origin
Разработчики часто сталкиваются с различными CORS-ошибками. Рассмотрим наиболее распространенные проблемы и способы их решения.
Проблема №1: "No 'Access-Control-Allow-Origin' header is present" Самая распространенная ошибка, указывающая на полное отсутствие CORS-заголовков в ответе сервера.
- Решение: Добавьте соответствующий заголовок в ответы вашего сервера, как показано в разделе настроек выше.
- Проверка: Используйте инструменты разработчика в браузере для просмотра заголовков ответа и убедитесь, что заголовок действительно добавляется.
Проблема №2: "The value of the 'Access-Control-Allow-Origin' header does not match the supplied origin" Возникает, когда значение заголовка не соответствует домену, с которого выполняется запрос.
- Решение: Настройте динамическое формирование заголовка на основе Origin запроса с проверкой против списка разрешенных доменов.
- Ловушка: Убедитесь, что проверка учитывает протокол (http/https) и наличие/отсутствие "www".
Проблема №3: "Request header field [имя_заголовка] is not allowed by Access-Control-Allow-Headers" Появляется при использовании нестандартных заголовков, которые не включены в список разрешенных.
- Решение: Добавьте необходимые заголовки в Access-Control-Allow-Headers.
- Пример:
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
Проблема №4: "Method [метод] is not allowed by Access-Control-Allow-Methods" Возникает при использовании HTTP-методов, не указанных в списке разрешенных.
- Решение: Расширьте список методов в заголовке Access-Control-Allow-Methods.
- Важно: Для каждого нестандартного метода (кроме GET, POST и HEAD) браузер отправляет предварительный запрос OPTIONS.
Проблема №5: "Credentials flag is true, but Access-Control-Allow-Origin is not the request origin" Возникает при использовании credentials (cookies, HTTP-аутентификации) с неправильно настроенными CORS-заголовками.
- Решение: Убедитесь, что установлены оба заголовка: Access-Control-Allow-Credentials: true и Access-Control-Allow-Origin с конкретным доменом (не "*").
- Клиентская сторона: Не забудьте установить {credentials: 'include'} в fetch или withCredentials: true в XMLHttpRequest.
Временные решения для разработки: Иногда нужно быстро обойти CORS-ограничения в процессе разработки:
- Использование браузерных расширений, отключающих проверку CORS (только для локальной разработки!).
- Запуск браузера с отключенной безопасностью (например, Chrome с флагом --disable-web-security).
- Использование прокси на стороне разработки (например, через настройку proxy в package.json для create-react-app).
- Локальный прокси-сервер (например, с помощью инструментов вроде cors-anywhere).
Важно помнить, что эти временные решения подходят только для разработки и тестирования. В продакшене всегда необходимо корректно настраивать CORS-политику на сервере. 🛠️
Управление CORS-политиками через заголовок Access-Control-Allow-Origin — это не просто техническая необходимость, а стратегический элемент безопасности современных веб-приложений. Правильно настроенный CORS защищает ваших пользователей, одновременно обеспечивая необходимую функциональность. Главный принцип: настраивайте максимально ограничительную политику, которая при этом позволяет вашему приложению корректно функционировать. Помните, что безопасность по своей природе противоречит удобству, и ваша задача — найти оптимальный баланс между этими двумя полюсами.