CORS
CORS — совместное использование ресурсов между источниками
TL;DR
CORS (Cross-Origin Resource Sharing) — механизм браузера, позволяющий серверу контролируемо разрешать кросс-доменные запросы из веб-приложений. Без правильной настройки CORS браузер блокирует запросы к API на другом домене.
Краткое определение
CORS — механизм безопасности браузера, который ограничивает или разрешает запросы к ресурсам на другом домене/порту/протоколе на основе заголовков, отправленных сервером.
Оригинал и перевод
- Язык: английский
- Оригинал: CORS
- Расшифровка: Cross-Origin Resource Sharing
- Буквальный перевод: обмен ресурсами между разными источниками
- Русские аналоги: кросс-доменные запросы, разделение ресурсов
Синонимы и варианты написания
- Cross-Origin Resource Sharing
- Кросс-доменная политика
- Same-Origin Policy (противоположность)
Что такое Origin
Origin = протокол + домен + порт:
https://example.com:443 ← Origin ├──────┘ └─────┘ └──┘ протокол домен порт https://api.example.com ≠ https://example.com (разные домены) https://example.com:8080 ≠ https://example.com (разные порты) http://example.com ≠ https://example.com (разные протоколы)
Где используется
- SPA приложения: React/Vue/Astro фронтенд на отдельном домене обращается к API
- REST/GraphQL API: публичные API для браузерных клиентов
- Web fonts: загрузка шрифтов с CDN
- WebGL textures: загрузка изображений для canvas
- Fetch/XMLHttpRequest: кросс-доменные AJAX запросы
Когда это важно
CORS критичен в следующих сценариях:
- Фронтенд и API на разных доменах:
https://mysite.com→https://api.mysite.com - Разработка на localhost:
http://localhost:3000→http://localhost:8080 - Интеграции с третьими сторонами: виджеты, iframe, внешние API
- Миграция с поддомена:
www.site.ru→site.ru(разные Origin!)
Как работает CORS
Простой запрос (Simple Request)
Браузер отправляет запрос с заголовком Origin и проверяет ответ:
# Запрос от браузера GET /api/data HTTP/1.1 Origin: https://frontend.com # Ответ от сервера Access-Control-Allow-Origin: https://frontend.com Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization
Браузер проверяет:
- Есть ли заголовок
Access-Control-Allow-Origin? - Совпадает ли он с
Originзапроса (или*)? - Если нет — блокирует ответ с ошибкой CORS.
Preflight запрос (OPTIONS)
Для сложных запросов браузер сначала отправляет OPTIONS:
# Preflight запрос OPTIONS /api/data HTTP/1.1 Origin: https://frontend.com Access-Control-Request-Method: POST Access-Control-Request-Headers: Content-Type, Authorization # Ответ сервера HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://frontend.com Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400 ← кэш preflight на 24 часа
Настройка CORS на сервере
Nginx: разрешить CORS для API
location /api/ {
# Динамический Origin (проверяйте в production!)
if ($http_origin ~* "^https://(www\.)?mysite\.com$") {
add_header Access-Control-Allow-Origin $http_origin;
}
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Max-Age 86400;
# Обработка preflight
if ($request_method = OPTIONS) {
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
proxy_pass http://backend;
}
PHP: заголовки CORS
<?php
// Разрешить только конкретному домену
$allowedOrigin = 'https://frontend.com';
if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN'] === $allowedOrigin) {
header("Access-Control-Allow-Origin: {$allowedOrigin}");
}
header('Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');
// Preflight запрос
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
Node.js (Express): cors middleware
const cors = require('cors');
app.use(cors({
origin: 'https://frontend.com',
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
maxAge: 86400
}));
Заголовки CORS
Заголовки запроса (от браузера)
| Заголовок | Описание |
|---|---|
Origin | Домен, с которого сделан запрос |
Access-Control-Request-Method | Метод запроса (для preflight) |
Access-Control-Request-Headers | Заголовки запроса (для preflight) |
Заголовки ответа (от сервера)
| Заголовок | Описание |
|---|---|
Access-Control-Allow-Origin | Какой домен разрешён (или *) |
Access-Control-Allow-Methods | Разрешённые методы |
Access-Control-Allow-Headers | Разрешённые заголовки |
Access-Control-Allow-Credentials | Разрешить cookies (true/false) |
Access-Control-Max-Age | Сколько кэшировать preflight (секунды) |
Access-Control-Expose-Headers | Какие заголовки показать JS |
Типичные ошибки CORS
- ❌
Access-Control-Allow-Origin: *с credentials — нельзя использовать вместе - ❌ Несколько доменов через запятую — браузер примет только один
- ❌ CORS защищает сервер — нет, это ограничение браузера, сервер должен сам проверять авторизацию
- ❌ Блокировка в production — на localhost работало, а на проде нет (разные Origin)
- ❌ Кэширование preflight слишком короткое — лишний OPTIONS запрос на каждый запрос
Аналоги и связанные термины
- Same-Origin Policy — базовая политика безопасности (запрещает кросс-доменные запросы)
- CSRF —跨站请求伪造 (другая уязвимость, часто путают с CORS)
- JSONP — устаревший обход Same-Origin Policy (только GET)
- Proxy server — обход CORS через прокси на том же домене
Смотри также (статьи на сайте)
- Nginx: полный гайд — настройка server blocks
- Безопасность веб-приложений — защита от уязвимостей
Смотри также (сниппеты)
- Nginx: CORS для API — готовая конфигурация
- PHP: CORS middleware — обработка preflight
Смотри также (термины)
- CSRF —跨站请求伪造 (другая уязвимость)
- Same-Origin Policy — базовая политика безопасности
- API — интерфейс программирования приложений
Мини-FAQ
CORS и CSRF — одно и то же?
Ответ: Нет. CORS — механизм браузера для разрешения кросс-доменных запросов. CSRF — уязвимость, когда чужой сайт заставляет браузер отправить запрос на ваш сервер. Это разные вещи.
Можно ли отключить CORS в браузере?
Ответ: Можно в режиме разработчика (флаги --disable-web-security), но никогда не делайте это в production. CORS защищает пользователей.
Почему на localhost работает, а на проде нет?
Ответ: Потому что http://localhost:3000 и https://mysite.com — разные Origin. Настройте CORS для production домена.
Access-Control-Allow-Origin: * — это безопасно?
Ответ: Для публичных API — да. Но если используете Access-Control-Allow-Credentials: true, то * нельзя — нужно указать конкретный домен.
Как отладить CORS ошибки?
Ответ:
- Откройте DevTools → Network
- Найдите запрос с ошибкой CORS
- Проверьте заголовки запроса (
Origin) и ответа (Access-Control-Allow-Origin) - Убедитесь, что они совпадают