البرمجة متوسط 9 دقيقة

كيفية إعداد CORS لواجهة API بالشكل الصحيح

CORS (مشاركة الموارد عبر الأصول) تُطبَّق من قِبَل المتصفح، ليس الخادم. واجهة API الخاصة بك لا "تحجب" الطلبات العابرة للأصول — المتصفح يفعل ذلك، بناءً على الترويسات التي يوفرها خادمك. فهم هذا التمييز هو الخطوة الأولى للإعداد الصحيح، وللمعرفة متى يكون خطأ CORS في الحقيقة مشكلة مختلفة في مظهر آخر.

الخطوات

  1. 1

    افهم ما تُطبّقه CORS فعلاً

    عندما يُرسل المتصفح طلباً عبر أصول مختلفة (نطاق أو منفذ أو مخطط مختلف)، يتحقق من الاستجابة بحثاً عن Access-Control-Allow-Origin. إذا كانت الترويسة مفقودة أو لا تطابق الأصل الطالب، يحجب المتصفح الاستجابة من الوصول إلى JavaScript — لكن الطلب وصل إلى خادمك والخادم استجاب فعلاً.

    هذا يعني أن CORS ليست آلية أمان لخادمك — إنها سياسة متصفح تحمي المستخدمين من المواقع الخبيثة التي تُرسل طلبات نيابةً عنهم. الطلبات من خادم لخادم (curl أو Postman أو خادم خلفي آخر) لا تتأثر بـ CORS أبداً.

  2. 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. 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. 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. 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. 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. 7

    تثبيت وإعداد cors في Express

    ثبّت حزمة cors وطبّقها في أبكر وقت ممكن في سلسلة الـ middleware — قبل أي مسارات.

    javascript
    npm 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. 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 أبداً على النقاط المصادَق عليها. خمس دقائق من الإعداد الصحيح تُوفّر ساعات من التشخيص المحيّر.

#API #CORS #Security
العودة إلى جميع الأدلة

هل تحتاج مساعدة في مشروعك؟

احجز استشارة مجانية لمدة 30 دقيقة لمناقشة تحدياتك التقنية واستكشاف الحلول معًا.