Полное руководство по решению проблемы с отправкой cookie в междоменных запросах (CORS)

Если вы столкнулись со сценарием, когда сервер правильно устанавливает cookie (видно в ответе браузера), но в последующих запросах браузер не возвращает эту cookie серверу, это руководство для вас. Эта проблема почти всегда возникает, когда фронтенд и бэкенд вашего проекта находятся на двух разных доменах или портах — например, фронтенд на app.example.com, а API на api.example.com.

Почему это происходит?

Современные браузеры применяют две строгие политики безопасности для предотвращения CSRF-атак и угона сессий:

  1. Политика CORS: По умолчанию браузер не позволяет междоменным запросам включать учетные данные (например, cookie), если сервер явно не разрешает это.
  2. Политика 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 с глубоким опытом в устранении неполадок и обеспечении безопасности веб-приложений готова помочь вам решить проблему от корня.

 

Помог ли вам данный ответ? 112 Пользователи нашли это полезным (112 голосов)