الدليل الشامل لحل مشكلة عدم إرسال ملفات تعريف الارتباط (الكوكيز) في طلبات Cross-Origin (CORS)
إذا واجهت سيناريو حيث يقوم الخادم بتعيين الكوكي بشكل صحيح (يمكن رؤيته في استجابة المتصفح)، ولكن في الطلبات اللاحقة، لا يقوم المتصفح بإرجاع هذا الكوكي إلى الخادم، فهذا الدليل مناسب لك. تحدث هذه المشكلة دائمًا تقريبًا عندما يكون الواجهة الأمامية والخلفية لمشروعك على نطاقين أو منفذين مختلفين — على سبيل المثال، الواجهة الأمامية على app.example.com وAPI على api.example.com.
لماذا يحدث هذا؟
تفرض المتصفحات الحديثة سياسيتين أمنيتين صارمتين لمنع هجمات CSRF واختطاف الجلسات:
- سياسة CORS: بشكل افتراضي، لا يسمح المتصفح للطلبات عبر النطاقات بتضمين بيانات الاعتماد (مثل الكوكيز) ما لم يصرح الخادم بذلك صراحةً.
- سياسة SameSite: بدءًا من Chrome 80، يتم التعامل مع الكوكيز بسلوك
SameSite=Laxافتراضيًا، مما يعني أنها لن تُرسل في الطلبات عبر النطاقات ما لم يتم تعريفها صراحةً بـSameSite=None.
النقطة المهمة هي أنه لحل هذه المشكلة، يجب تكوين كل من جانب العميل والخادم بشكل صحيح. لا يكفي تعديل جانب واحد فقط.
الخطوة الأولى: تكوين الخادم بشكل صحيح
ثلاث قواعد ذهبية في جانب الخادم
القاعدة الأولى — تمكين بيانات الاعتماد: يجب أن يحتوي رأس استجابة الخادم على القيمة التالية:
Access-Control-Allow-Credentials: true
القاعدة الثانية — عدم استخدام Wildcard: عند تمكين بيانات الاعتماد، لا يمكنك استخدام علامة * في رأس 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, // منع الوصول إلى الكوكي عبر JavaScript
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، فاضبط supports_credentials على true في ملف config/cors.php وأضف نطاق الواجهة الأمامية إلى مصفوفة 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). يجب أن يتم الرد على هذا الطلب برمز 200 أو 204 ورؤوس 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)، فإن الخوادم الافتراضية رادیب مع الأجهزة القوية والشبكة المستقرة هي الخيار الأفضل لواجهات برمجة التطبيقات الاحترافية.
خدمات تصحيح الأخطاء والأمن رادیب — إذا كنت تتعامل مع أخطاء CORS المعقدة أو مشاكل المصادقة أو التحديات الأمنية، فإن فريق رادیب الفني، بخبرة عميقة في استكشاف الأخطاء وتأمين تطبيقات الويب، موجود لمساعدتك في حل المشكلة من جذورها.


