راهنمای جامع رفع مشکل عدم ارسال کوکی در درخواست‌های Cross-Origin (CORS)

اگر شما هم با این سناریو مواجه شدید که سرور کوکی را به درستی ست می‌کند (در پاسخ مرورگر قابل مشاهده است)، اما در درخواست‌های بعدی، مرورگر آن کوکی را به سرور بازنمی‌گرداند، این راهنما برای شماست. این مشکل تقریباً همیشه زمانی رخ می‌دهد که فرانت‌اند و بک‌اند پروژه شما روی دو دامنه یا پورت متفاوت قرار دارند — مثلاً فرانت‌اند روی app.example.com و API روی api.example.com.

چرا این اتفاق می‌افتد؟

مرورگرهای مدرن برای جلوگیری از حملات CSRF و سرقت نشست کاربران (Session Hijacking)، دو سیاست امنیتی سخت‌گیرانه اعمال می‌کنند:

  1. سیاست CORS: به‌صورت پیش‌فرض، مرورگر اجازه نمی‌دهد درخواست‌های بین‌دامنه‌ای همراه با اطلاعات اعتبارسنجی (مانند کوکی) ارسال شوند، مگر اینکه سرور صراحتاً این اجازه را صادر کرده باشد.
  2. سیاست SameSite: از نسخه ۸۰ کروم به بعد، کوکی‌ها به‌صورت پیش‌فرض با رفتار SameSite=Lax تلقی می‌شوند؛ یعنی در درخواست‌های بین‌دامنه‌ای ارسال نخواهند شد، مگر اینکه صراحتاً با SameSite=None تعریف شده باشند.

نکته مهم این است که برای حل این مشکل، هر دو سمت کلاینت و سرور باید به درستی پیکربندی شوند. تنها اصلاح یک سمت کافی نیست.

گام اول: پیکربندی صحیح سرور

سه قانون طلایی در سمت سرور

قانون اول — فعال‌سازی Credentials: هدر پاسخ سرور باید شامل مقدار زیر باشد:

Access-Control-Allow-Credentials: true

قانون دوم — عدم استفاده از Wildcard: هنگامی که credentials فعال است، دیگر نمی‌توانید در هدر Access-Control-Allow-Origin از کاراکتر * استفاده کنید. باید دقیقاً آدرس کامل دامنه فرانت‌اند را مشخص کنید:

Access-Control-Allow-Origin: https://app.example.com

قانون سوم — ویژگی‌های کوکی: کوکی باید با دو ویژگی SameSite=None و Secure ست شود. بدون این دو ویژگی، مرورگر کوکی را ذخیره می‌کند اما در درخواست‌های بین‌دامنه‌ای هرگز آن را ارسال نمی‌کند.

نمونه کد در 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,   // جلوگیری از دسترسی جاوااسکریپت به کوکی
    secure: true,     // فقط از طریق HTTPS ارسال شود
    sameSite: 'none', // اجازه ارسال در درخواست‌های Cross-Origin
    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");

// پاسخ به درخواست Preflight
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

// ست کردن کوکی با ویژگی‌های لازم
setcookie('session_token', 'abc123', [
    'expires'  => time() + 86400,
    'path'     => '/',
    'secure'   => true,      // الزامی
    'httponly' => true,
    'samesite' => 'None'     // الزامی برای Cross-Origin
]);

نکته: اگر از Laravel استفاده می‌کنید، در فایل config/cors.php مقدار supports_credentials را true و دامنه فرانت‌اند را در آرایه allowed_origins قرار دهید.

گام دوم: پیکربندی صحیح کلاینت (فرانت‌اند)

حتی با پیکربندی کامل سرور، مرورگر همچنان کوکی‌ها را ارسال نمی‌کند مگر اینکه در سمت کلاینت نیز صراحتاً اعلام کرده باشید که این درخواست «همراه با اعتبارنامه» است.

با استفاده از 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 یعنی کوکی فقط روی اتصال رمزنگاری‌شده کار می‌کند. اگر سایت شما روی HTTP است، مرورگر کوکی را نمی‌پذیرد. (استثنا: محیط توسعه روی localhost)
  • آدرس Origin دقیق است؟ آدرس Access-Control-Allow-Origin باید عیناً با آدرسی که در مرورگر می‌بینید یکسان باشد. حتی تفاوت در www یا اسلش پایانی / باعث شکست می‌شود.
  • درخواست OPTIONS موفق است؟ در تب Network ابزار توسعه مرورگر، درخواست Preflight (با متد OPTIONS) را بررسی کنید. این درخواست باید با کد ۲۰۰ یا ۲۰۴ و هدرهای صحیح CORS پاسخ داده شود.
  • کوکی ذخیره شده است؟ در مرورگر به مسیر DevTools ← Application ← Cookies بروید و مطمئن شوید کوکی با ویژگی‌های Secure و SameSite=None ذخیره شده است.
  • پروکسی یا فایروال واسطه ندارید؟ گاهی Cloudflare یا وب‌سرورهایی مانند Nginx هدرها را بازنویسی می‌کنند. پیکربندی آن‌ها را نیز بررسی کنید.

جمع‌بندی

ارسال کوکی در درخواست‌های بین‌دامنه‌ای نیازمند هماهنگی سه عامل است: سرور با هدرهای Access-Control-Allow-Credentials: true و Origin دقیق، کوکی با ویژگی‌های SameSite=None; Secure، و کلاینت با فعال‌سازی credentials: 'include' یا withCredentials: true. با رعایت این سه نکته، مشکل به‌طور کامل برطرف می‌شود.


زیرساخت مطمئن، خیال آسوده برای توسعه‌دهندگان

پیاده‌سازی صحیح CORS و مدیریت کوکی‌ها تنها بخشی از ماجراست؛ زیرساختی که پروژه شما روی آن اجرا می‌شود نیز نقش تعیین‌کننده‌ای در پایداری و امنیت سرویس دارد:

هاست رادیب — میزبانی وب پرسرعت با پشتیبانی کامل از SSL رایگان و پیکربندی استاندارد هدرها؛ گزینه‌ای ایده‌آل برای میزبانی فرانت‌اند و بک‌اند پروژه‌های شما.

سرور مجازی رادیب — اگر پروژه شما به دسترسی کامل، منابع اختصاصی و آزادی در پیکربندی وب‌سرور (Nginx/Apache) نیاز دارد، سرورهای مجازی رادیب با سخت‌افزار قدرتمند و شبکه پایدار، بهترین انتخاب برای APIهای حرفه‌ای است.

خدمات رفع باگ و امنیت رادیب — اگر با خطاهای پیچیده CORS، مشکلات احراز هویت یا چالش‌های امنیتی دست‌وپنجه نرم می‌کنید، تیم فنی رادیب با تجربه عمیق در عیب‌یابی و امن‌سازی وب‌اپلیکیشن‌ها، در کنار شماست تا مشکل را از ریشه حل کند.

 

آیا این پاسخ به شما کمک کرد؟ 112 کاربر این را مفید یافتند (112 نظرات)