الخطوات
-
1
افهم ما تُطبّقه CORS فعلاً
عندما يُرسل المتصفح طلباً عبر أصول مختلفة (نطاق أو منفذ أو مخطط مختلف)، يتحقق من الاستجابة بحثاً عن
Access-Control-Allow-Origin. إذا كانت الترويسة مفقودة أو لا تطابق الأصل الطالب، يحجب المتصفح الاستجابة من الوصول إلى JavaScript — لكن الطلب وصل إلى خادمك والخادم استجاب فعلاً.هذا يعني أن CORS ليست آلية أمان لخادمك — إنها سياسة متصفح تحمي المستخدمين من المواقع الخبيثة التي تُرسل طلبات نيابةً عنهم. الطلبات من خادم لخادم (curl أو Postman أو خادم خلفي آخر) لا تتأثر بـ CORS أبداً.
-
2
لا تستخدم wildcard للطلبات المصادَق عليها
ضبط
Access-Control-Allow-Origin: *يسمح لأي أصل بقراءة الاستجابات، لكن المتصفح سيرفض إرسال الكوكيز أو ترويساتAuthorizationإلى أصل wildcard. إذا اعتمدت واجهة API على كوكيز الجلسة أو الـ tokens، لا يمكن للـ wildcard أن يعمل — ولا ينبغي أن تريده، إذ سيسمح لأي موقع بإجراء استدعاءات مصادَق عليها نيابةً عن مستخدميك.bash// لا تستخدم هذا لواجهات API المصادَق عليها Access-Control-Allow-Origin: * // الصحيح: حدد الأصل بدقة Access-Control-Allow-Origin: https://app.example.com -
3
طبّق قائمة بيضاء من الأصول الموثوقة
لمعظم واجهات API لديك مجموعة صغيرة ومعروفة من الأصول (الواجهة الأمامية، لوحة الإدارة، ربما عرض ويب للجوال). تحقق من ترويسة
Originللطلب مقابل تلك القائمة وأرجعها فقط إذا تطابقت.javascript// Node.js / Express — cors middleware const cors = require('cors'); const allowedOrigins = [ 'https://app.example.com', 'https://admin.example.com', ]; app.use(cors({ origin: (origin, callback) => { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error(`CORS: الأصل ${origin} غير مسموح به`)); } }, credentials: true, })); -
4
التعامل مع طلبات OPTIONS التمهيدية (Preflight)
للطلبات التي تستخدم methods غير بسيطة (PUT أو DELETE أو PATCH) أو ترويسات مخصصة، يُرسل المتصفح طلب
OPTIONSتمهيدياً أولاً. يجب أن يستجيب خادمك بـ 200 أو 204 مع ترويسات CORS المناسبة — وإلا لن يُرسَل الطلب الفعلي أبداً.bash// Express: middleware cors() يتعامل مع OPTIONS تلقائياً. // إذا كنت تتعامل مع CORS يدوياً، أضف هذا: app.options('*', cors(corsOptions)); // يجب أن تتضمن استجابة preflight: Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400 -
5
اضبط Allow-Credentials للمصادقة بالكوكيز
إذا استخدمت واجهة API كوكيز الجلسة أو تطلّب من المتصفح إرسال كوكيز مخزنة، يجب ضبط
Access-Control-Allow-Credentials: trueمع تحديد أصل معين (غير wildcard). كذلك يحتاج العميل إلى ضبطcredentials: 'include'في استدعاء fetch.javascript// ترويسة الاستجابة Access-Control-Allow-Credentials: true // جانب العميل (fetch) fetch('https://api.example.com/me', { method: 'GET', credentials: 'include', }); // جانب العميل (axios) axios.get('https://api.example.com/me', { withCredentials: true }); -
6
إعداد CORS في Laravel
يُشحن Laravel مع ملف
config/cors.phpومع middleware الـHandleCorsمسجّلاً عالمياً. عدّل الإعداد — لا تكتب middleware مخصصاً.php// config/cors.php return [ 'paths' => ['api/*'], 'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], 'allowed_origins' => ['https://app.example.com', 'https://admin.example.com'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['Content-Type', 'Authorization', 'Accept'], 'exposed_headers' => [], 'max_age' => 86400, 'supports_credentials' => true, ]; -
7
تثبيت وإعداد cors في Express
ثبّت حزمة
corsوطبّقها في أبكر وقت ممكن في سلسلة الـ middleware — قبل أي مسارات.javascriptnpm install cors // app.js const cors = require('cors'); const corsOptions = { origin: ['https://app.example.com', 'https://admin.example.com'], methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, maxAge: 86400, }; app.use(cors(corsOptions)); app.options('*', cors(corsOptions)); -
8
تشخيص خطأ "Access-Control-Allow-Origin" في التطوير
أثناء التطوير تعمل واجهتك الأمامية على
http://localhost:3000وواجهة API علىhttp://localhost:8000. هذا زوج عبر أصول مختلفة. أضفhttp://localhost:3000(أو منفذ التطوير لديك) إلى الأصول المسموح بها — لا تضفlocalhostبدون منفذ لأن المنفذ جزء من الأصل. أزله قبل النشر للإنتاج.إذا استمر الخطأ بعد إصلاح الترويسة، تحقق من تبويب Network في أدوات المطور: انظر إلى ترويسات الاستجابة الفعلية على الطلب الفاشل للتأكد من أن خادمك يُرسل الأصل الصحيح. الـ reverse proxy المُعدّ بشكل خاطئ كثيراً ما يحذف ترويسات CORS أو يُعيد كتابتها بصمت.
نصائح ومحاذير
- أخطاء CORS في المتصفح لا تعني أن واجهة API آمنة — تعني فقط أن المتصفح رفض تسليم الاستجابة لـ JavaScript. عميل HTTP مباشر (curl أو Postman أو كود خادم) يتجاوز CORS بالكامل.
- احتفظ بقائمة الأصول المسموح بها في متغير بيئة كي تختلف بين التطوير والاختبار والإنتاج دون تغييرات في الكود.
- لا تضبط <code>Access-Control-Allow-Headers: *</code> عند تفعيل الـ credentials — ترويسات الـ wildcard غير مدعومة مع الطلبات المصادَق عليها في كثير من المتصفحات.
- <code>Access-Control-Max-Age</code> الطويل (86400 = 24 ساعة) يُقلل تكلفة طلبات preflight بشكل ملحوظ لواجهات API ذات الحركة المرورية العالية.
خاتمة
إعداد CORS ليس معقداً بمجرد أن تفهم أن المتصفح هو المُطبِّق، وليس خادمك. احتفظ بقائمة الأصول المسموح بها صغيرة وصريحة، وفعّل الـ credentials فقط عند الحاجة، وتعامل دائماً مع طلبات preflight، ولا تستخدم wildcard أبداً على النقاط المصادَق عليها. خمس دقائق من الإعداد الصحيح تُوفّر ساعات من التشخيص المحيّر.