Полное руководство по решению проблемы с отправкой cookie в междоменных запросах (CORS)
Если вы столкнулись со сценарием, когда сервер правильно устанавливает cookie (видно в ответе браузера), но в последующих запросах браузер не возвращает эту cookie серверу, это руководство для вас. Эта проблема почти всегда возникает, когда фронтенд и бэкенд вашего проекта находятся на двух разных доменах или портах — например, фронтенд на app.example.com, а API на api.example.com.
Почему это происходит?
Современные браузеры применяют две строгие политики безопасности для предотвращения CSRF-атак и угона сессий:
- Политика CORS: По умолчанию браузер не позволяет междоменным запросам включать учетные данные (например, cookie), если сервер явно не разрешает это.
- Политика SameSite: Начиная с Chrome 80, cookie по умолчанию обрабатываются с поведением
SameSite=Lax, что означает, что они не будут отправляться в междоменных запросах, если они явно не определены сSameSite=None.
Важно отметить, что для решения этой проблемы и клиентская, и серверная стороны должны быть правильно настроены. Только изменение одной стороны недостаточно.
Шаг 1: Правильная настройка сервера
Три золотых правила на стороне сервера
Правило 1 — Включение учетных данных: Заголовок ответа сервера должен содержать следующее значение:
Access-Control-Allow-Credentials: true
Правило 2 — Без подстановочных знаков: Когда учетные данные включены, вы не можете использовать символ подстановки * в заголовке Access-Control-Allow-Origin. Вы должны точно указать полный адрес домена фронтенда:
Access-Control-Allow-Origin: https://app.example.com
Правило 3 — Атрибуты cookie: Cookie должна быть установлена с двумя атрибутами: SameSite=None и Secure. Без этих двух атрибутов браузер сохранит cookie, но никогда не отправит ее в междоменных запросах.
Пример кода в Node.js (Express)
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://app.example.com', // Точный домен фронтенда, без /
credentials: true
}));
app.post('/login', (req, res) => {
res.cookie('session_token', 'abc123', {
httpOnly: true, // Предотвращает доступ JavaScript к cookie
secure: true, // Отправляется только через HTTPS
sameSite: 'none', // Разрешает отправку в междоменных запросах
maxAge: 24 * 60 * 60 * 1000
});
res.json({ message: 'Вход выполнен успешно' });
});
Пример кода в PHP
<?php
// Укажите точный домен фронтенда, не *
header("Access-Control-Allow-Origin: https://app.example.com");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
// Ответ на предварительный запрос
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
// Установка cookie с необходимыми атрибутами
setcookie('session_token', 'abc123', [
'expires' => time() + 86400,
'path' => '/',
'secure' => true, // Обязательно
'httponly' => true,
'samesite' => 'None' // Обязательно для междоменных запросов
]);
Примечание: Если вы используете Laravel, установите supports_credentials в true в файле config/cors.php и добавьте домен фронтенда в массив allowed_origins.
Шаг 2: Правильная настройка клиента (фронтенда)
Даже при полной настройке сервера браузер все равно не будет отправлять cookie, если вы явно не объявите на стороне клиента, что этот запрос "с учетными данными".
Использование Fetch API
fetch('https://api.example.com/user/profile', {
method: 'GET',
credentials: 'include' // Эта строка является ключом к решению проблемы
})
.then(res => res.json())
.then(data => console.log(data));
Использование Axios
// Для конкретного запроса
axios.get('https://api.example.com/user/profile', {
withCredentials: true
});
// Или глобально для всего проекта
axios.defaults.withCredentials = true;
Контрольный список для устранения неполадок
Если проблема сохраняется после применения вышеуказанных мер, проверьте эти пункты по порядку:
- ✅ Обе стороны находятся на HTTPS? Атрибут
Secureозначает, что cookie работает только через зашифрованные соединения. Если ваш сайт на HTTP, браузер не примет cookie. (Исключение: среда разработки наlocalhost) - ✅ Точен ли адрес Origin? Адрес
Access-Control-Allow-Originдолжен точно совпадать с URL, который вы видите в браузере. Даже разница вwwwили завершающая косая черта/вызовет ошибку. - ✅ Успешен ли запрос OPTIONS? На вкладке Network в инструментах разработчика браузера проверьте предварительный запрос (с методом OPTIONS). Этот запрос должен быть отвечен кодом 200 или 204 и правильными заголовками CORS.
- ✅ Сохранена ли cookie? В браузере перейдите в DevTools ← Application ← Cookies и убедитесь, что cookie сохранена с атрибутами
SecureиSameSite=None. - ✅ Есть ли прокси или промежуточный брандмауэр? Иногда Cloudflare или веб-серверы, такие как Nginx, перезаписывают заголовки. Проверьте также их конфигурацию.
Резюме
Отправка cookie в междоменных запросах требует координации трех факторов: сервер с Access-Control-Allow-Credentials: true и точным Origin, cookie с атрибутами SameSite=None; Secure и клиент с активацией credentials: 'include' или withCredentials: true. Следуя этим трем пунктам, проблема будет полностью решена.
Надежная инфраструктура, спокойствие для разработчиков
Правильная реализация CORS и управление cookie — это лишь часть истории; инфраструктура, на которой работает ваш проект, также играет решающую роль в стабильности и безопасности сервиса:
Radib Hosting — Высокоскоростной веб-хостинг с полной поддержкой бесплатного SSL и стандартной конфигурацией заголовков; идеальный выбор для хостинга фронтенда и бэкенда ваших проектов.
Radib Virtual Server — Если ваш проект требует полного доступа, выделенных ресурсов и свободы в настройке веб-сервера (Nginx/Apache), виртуальные серверы Radib с мощным оборудованием и стабильной сетью являются лучшим выбором для профессиональных API.
Radib Debugging and Security Services — Если вы сталкиваетесь со сложными ошибками CORS, проблемами аутентификации или проблемами безопасности, техническая команда Radib с глубоким опытом в устранении неполадок и обеспечении безопасности веб-приложений готова помочь вам решить проблему от корня.


