Исправляем CORS: безопасные заголовки и примеры настройки
#Веб-разработка #Web API #Веб-безопасностьДля кого эта статья:
- Веб-разработчики, работающие с API и микросервисами
- Специалисты по DevOps и серверной инфраструктуре
- Фронтенд-разработчики, сталкивающиеся с ошибками CORS в своих проектах
Работая с современными веб-приложениями, многие разработчики рано или поздно встречают её — загадочную ошибку CORS, способную остановить разработку проекта на несколько часов или даже дней. "Access to fetch at 'https://api.example.com' from origin 'https://yourapp.com' has been blocked by CORS policy" — знакомо, не правда ли? 😅 Эта ошибка не просто раздражает, она может стать критическим препятствием при интеграции API или создании микросервисной архитектуры. Давайте разберемся, как раз и навсегда победить эту головную боль веб-разработки и настроить безопасные заголовки CORS для вашего проекта.
Что такое CORS: причины блокировки межсайтовых запросов
Cross-Origin Resource Sharing (CORS) — механизм безопасности, встроенный в современные браузеры, который ограничивает веб-страницам отправку запросов к доменам, отличным от того, с которого была загружена страница. Это защитный барьер, предотвращающий вредоносные межсайтовые запросы.
По умолчанию браузеры блокируют такие запросы из соображений безопасности. Представьте, что вы вошли в свой онлайн-банкинг и оставили сессию открытой. Без CORS вредоносный сайт, открытый в соседней вкладке, мог бы отправлять запросы к API вашего банка, используя ваши сохраненные куки аутентификации. 🔒
CORS реализует политику одного источника (Same-Origin Policy), разрешая серверу явно указать, каким внешним источникам можно взаимодействовать с ресурсами.
Запрос считается межсайтовым, если отличается хотя бы один из элементов:
- Протокол (HTTP vs HTTPS)
- Домен (example.com vs api.example.com)
- Порт (example.com vs example.com:8080)
Алексей Петров, DevOps-инженер
Однажды я работал над проектом для крупного маркетплейса, где фронтенд и API были развернуты на разных поддоменах. Команда фронтенд-разработки жаловалась на "необъяснимые" проблемы с получением данных в production, хотя локально все работало идеально.
Корень проблемы крылся в том, что локально они тестировали всё на localhost, где домен был один и тот же для API и фронтенда. В production же фронтенд находился на shop.example.com, а API на api.example.com. Браузеры блокировали запросы из-за отсутствия правильных CORS-заголовков.
Разработчики просто не понимали, почему в production происходили ошибки. Мы потеряли почти неделю, прежде чем выяснили, что дело в неправильно настроенных CORS-заголовках. После добавления Access-Control-Allow-Origin: https://shop.example.com на сервере API, всё заработало как часы.
Когда браузер определяет межсайтовый запрос, он автоматически добавляет заголовок Origin, указывающий, откуда пришел запрос. Сервер, получив такой запрос, должен решить, разрешить его или нет, добавив соответствующие CORS-заголовки к своему ответу.
| Тип запроса | Характеристики | Обработка CORS |
|---|---|---|
| Простой | GET, POST, HEAD + стандартные заголовки | Запрос отправляется напрямую |
| Предварительный | PUT, DELETE, PATCH или нестандартные заголовки | Сначала OPTIONS-запрос для проверки |
| С учетными данными | Запросы с куками, HTTP-аутентификацией | Требует особых заголовков для разрешения |

Основные заголовки CORS для безопасного обмена данными
Правильная настройка CORS-заголовков — это баланс между безопасностью и функциональностью. Рассмотрим основные заголовки, которые вам потребуется настроить:
Access-Control-Allow-Origin — ключевой заголовок, определяющий, какие домены могут получать ответы от сервера. Имеет два варианта настройки:
Access-Control-Allow-Origin: *— разрешает запросы с любого домена (не рекомендуется для продакшн)Access-Control-Allow-Origin: https://trusted-site.com— разрешает запросы только с указанного домена
Access-Control-Allow-Methods — определяет, какие HTTP-методы разрешены при обращении к ресурсу:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers — указывает, какие HTTP-заголовки могут использоваться при запросе:
Access-Control-Allow-Headers: X-Custom-Header, Content-Type, Authorization
Access-Control-Allow-Credentials — разрешает передачу куков и данных аутентификации при межсайтовых запросах:
Access-Control-Allow-Credentials: true
⚠️ Важно: если установлено Access-Control-Allow-Credentials: true, то Access-Control-Allow-Origin не может быть установлен как * и должен указывать на конкретный домен.
Access-Control-Max-Age — указывает, как долго результаты предварительного запроса можно кэшировать (в секундах):
Access-Control-Max-Age: 3600
Access-Control-Expose-Headers — определяет, какие заголовки ответа будут доступны для JavaScript-кода на клиенте:
Access-Control-Expose-Headers: Content-Length, X-My-Custom-Header
Пример комплексной настройки безопасных CORS-заголовков:
Access-Control-Allow-Origin: https://trusted-client.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600
Access-Control-Expose-Headers: Content-Length, X-Request-ID
Типичные ошибки CORS и пошаговое решение проблем
При работе с CORS разработчики часто сталкиваются с рядом типичных ошибок. Рассмотрим их и предложим решения. 🔍
Марина Ковалева, фронтенд-разработчик
В нашем проекте мы использовали React для фронтенда и Node.js для API. Всё отлично работало на локальных машинах команды, но когда мы выкатили обновление на staging-среду, начались проблемы с CORS.
Самым неожиданным было то, что ошибки возникали только для определённых эндпоинтов, которые использовали PUT-запросы с JSON-данными. GET-запросы работали без проблем.
После долгих часов отладки выяснилось, что проблема была в том, что наш прокси-сервер Nginx не пропускал предварительные OPTIONS-запросы к API. Он их "проглатывал", не перенаправляя дальше.
Мы добавили в конфигурацию Nginx правило для обработки OPTIONS-запросов:
if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' 'https://staging.ourapp.com'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization'; add_header 'Access-Control-Max-Age' 86400; return 204; }Этот простой фикс решил проблему, но мы потратили на отладку целый день, потому что изначально искали ошибку не там. Теперь я всегда проверяю обработку OPTIONS-запросов в первую очередь.
Давайте рассмотрим наиболее частые ошибки CORS и пути их решения:
| Ошибка CORS | Причина | Решение |
|---|---|---|
| Origin not allowed | Значение Origin не совпадает с Access-Control-Allow-Origin | Добавить нужный домен в заголовок Access-Control-Allow-Origin |
| Method not allowed | Используемый HTTP-метод не указан в Access-Control-Allow-Methods | Добавить метод в Access-Control-Allow-Methods |
| Headers not allowed | Используемые заголовки не указаны в Access-Control-Allow-Headers | Добавить заголовки в Access-Control-Allow-Headers |
| Credentials not supported | Запрос с credentials, но сервер не разрешает их | Установить Access-Control-Allow-Credentials: true |
| Wildcards not allowed with credentials | Access-Control-Allow-Origin: * с Access-Control-Allow-Credentials: true | Заменить * на конкретный домен |
Пошаговый алгоритм отладки CORS-ошибок:
- Проверьте консоль браузера для точного определения ошибки CORS
- Определите, какие заголовки отсутствуют или настроены некорректно
- Проверьте, требуется ли обработка предварительных (preflight) запросов
- Убедитесь, что сервер корректно отвечает на OPTIONS-запросы
- Проверьте наличие посредников (прокси, CDN), которые могут влиять на заголовки
- Используйте инструменты разработчика для анализа запросов и ответов
- Добавляйте заголовки постепенно, проверяя эффект каждого изменения
Если вы используете сервис, где не можете напрямую настроить заголовки (например, сторонний API), рассмотрите возможность создания прокси-сервера на своей стороне, который будет добавлять нужные заголовки.
Настройка CORS на разных серверных платформах
Настройка CORS сильно зависит от используемой вами серверной технологии. Рассмотрим наиболее популярные варианты. 🛠️
Node.js (Express)
Для Express.js удобно использовать пакет cors:
const express = require('express');
const cors = require('cors');
const app = express();
// Базовая настройка для всех маршрутов
app.use(cors({
origin: 'https://trusted-site.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
// Или для конкретного маршрута
app.get('/api/data', cors({
origin: ['https://site1.com', 'https://site2.com']
}), (req, res) => {
// Обработка запроса
});
Python (Django)
В Django можно использовать пакет django-cors-headers:
# settings.py
INSTALLED_APPS = [
# ...
'corsheaders',
# ...
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# Важно: разместите над CommonMiddleware
'django.middleware.common.CommonMiddleware',
# ...
]
CORS_ALLOWED_ORIGINS = [
'https://trusted-site.com',
]
CORS_ALLOW_METHODS = [
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
]
CORS_ALLOW_CREDENTIALS = True
PHP
В чистом PHP заголовки можно добавить напрямую:
<?php
// Проверка Origin
$allowed_origins = ['https://trusted-site.com', 'https://another-site.com'];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowed_origins)) {
header("Access-Control-Allow-Origin: $origin");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Allow-Credentials: true");
}
// Для OPTIONS-запросов возвращаем 200 OK без содержимого
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
// Далее ваш основной код
Nginx
Для Nginx настройка выполняется в конфигурационном файле сервера:
server {
# ...
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://trusted-site.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Max-Age' '1728000';
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' '0';
return 204;
}
add_header 'Access-Control-Allow-Origin' 'https://trusted-site.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Allow-Credentials' 'true';
proxy_pass http://backend;
}
# ...
}
Apache
Для Apache можно использовать файл .htaccess или настройку VirtualHost:
# .htaccess
<IfModule mod_headers.c>
SetEnvIf Origin "https://trusted-site.com" ALLOWED_ORIGIN=$0
Header set Access-Control-Allow-Origin "%{ALLOWED_ORIGIN}e" env=ALLOWED_ORIGIN
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type, Authorization"
Header set Access-Control-Allow-Credentials "true"
# Ответ на preflight запросы
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>
Тестирование и отладка CORS: инструменты разработчика
Для эффективной отладки CORS-проблем важно использовать правильные инструменты. Рассмотрим наиболее полезные из них. 🔧
Инструменты разработчика в браузере
Вкладка Network в DevTools — ваш основной инструмент для отладки:
- В Chrome: Ctrl+Shift+I (Windows/Linux) или Cmd+Option+I (Mac), затем вкладка Network
- В Firefox: Ctrl+Shift+E (Windows/Linux) или Cmd+Option+E (Mac)
Проверьте следующие моменты в журнале сетевых запросов:
- Есть ли предварительный OPTIONS-запрос перед основным запросом?
- Какие заголовки CORS присутствуют в ответе сервера?
- Возвращается ли для OPTIONS-запроса статус 200, 204 или 2xx?
- Содержит ли запрос заголовок Origin и совпадает ли он с разрешенным на сервере?
Curl для тестирования CORS-заголовков
Используйте curl для проверки OPTIONS-запросов:
curl -X OPTIONS https://your-api.com/endpoint \
-H "Origin: https://your-client-site.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
-v
Онлайн-инструменты для проверки CORS
- CORS Test Tool (https://cors-test.codehappy.dev/)
- Postman для тестирования API и анализа заголовков
- CORS Validator (https://cors-validator.com/)
Расширения для браузеров
Если вы не можете изменить серверную часть (например, при тестировании стороннего API), можно временно использовать расширения для обхода ограничений CORS:
- CORS Unblock для Chrome/Firefox
- Allow CORS: Access-Control-Allow-Origin для Chrome
⚠️ Важно: эти расширения следует использовать только для тестирования и отладки, но не в продакшн-окружении, так как они отключают важный механизм безопасности.
Чеклист для тестирования CORS-настроек
- Проверьте простые запросы (GET, POST) с разных доменов
- Проверьте сложные запросы (PUT, DELETE, с нестандартными заголовками)
- Протестируйте запросы с учетными данными (withCredentials: true)
- Проверьте работу с разными типами содержимого (application/json, multipart/form-data)
- Убедитесь, что предварительные запросы OPTIONS обрабатываются корректно
- Проверьте максимальное время кэширования предварительных запросов
- Протестируйте доступ с незащищенного (HTTP) и защищенного (HTTPS) соединения
Инструменты для автоматизированного тестирования CORS:
- Playwright/Puppeteer для автоматизации тестирования в браузере
- Jest с axios или fetch для тестирования запросов
- Cypress для E2E-тестирования с проверкой CORS-ситуаций
Разобравшись с принципами работы CORS и освоив правильную настройку заголовков, вы можете превратить потенциальную проблему в надежный механизм защиты ваших веб-приложений. Помните, что CORS — не просто препятствие, которое нужно преодолеть, а важный элемент безопасности, который, при правильной настройке, позволяет контролировать доступ к вашим ресурсам с других доменов. Используя подходы, описанные в этой статье, вы сможете эффективно настроить CORS-заголовки, избегая распространенных ошибок и обеспечивая безопасное взаимодействие между различными доменами в вашей инфраструктуре.
Элина Баранова
разработчик Android