لو فتحت الـ console في المتصفح ولقيت السطر الأحمر ده: Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy، يبقى أنت قدام أشهر مفاجأة بتحصل لأي مطور frontend جديد. السيرفر شغّال بدليل إنه بيرد على Postman، الكود سليم، ومع ذلك المتصفح بيرفض. السبب مش bug، السبب طبقة حماية اسمها CORS وانت محتاج تفهمها قبل ما تكتب أول API call في حياتك.
CORS للمبتدئ: ليه المتصفح بيرفض طلبك رغم إن السيرفر شغّال
المشكلة باختصار
لما تكتب fetch('https://api.example.com/users') من صفحة شغّالة على localhost:3000، المتصفح بيلاحظ إن الـ Origin بتاع الصفحة مش زي الـ Origin بتاع الـ API. الأولى http://localhost:3000، التانية https://api.example.com. ساعتها بيدخل في وضع تأمين خاص، وبيرفض يديلك الرد إلا لو السيرفر صرّح صراحة إنه موافق يستقبل طلبات من الـ Origin بتاعك. الرفض ده بيتم على مستوى المتصفح، مش على مستوى السيرفر. يعني السيرفر فعلاً استقبل الطلب ورد، بس المتصفح أكل الرد ومنعه يوصلك.
مثال بسيط: بوّاب البناية
تخيّل إنك ساكن في عمارة، والبوّاب عنده تعليمات صارمة: ميسمحش لحد من بنايات تانية يدخل شقتك، إلا لو صاحب الشقة سبق وقال للبوّاب صراحةً: "الراجل اللي اسمه أحمد من العمارة المقابلة مسموحله يدخل عندي". لو أحمد جه ومحدّش قال للبوّاب عنه قبل كده، البوّاب هيوقفه عند الباب ويرجّعه، حتى لو صاحب الشقة فاتح الباب وقال "تفضل ادخل".
المتصفح هو البوّاب. الصفحة شقتك. الـ API بتاعك بناية تانية. الـ Origin هو عنوان البناية. وقاعدة "مسموحله يدخل عندي" هي اللي بتترجم لـ HTTP header اسمه Access-Control-Allow-Origin. لو السيرفر مبعتش الـ header ده، المتصفح بيرجّع الرد على الباب وميديهوش لكودك، مهما كان الرد فيه إيه.
تعريف Origin بدقة
الـ Origin مش الدومين بس. هو 3 حاجات لازم يتطابقوا كلهم:
- الـ scheme (يعني
httpأوhttps) - الـ host (يعني
example.comأوapi.example.com) - الـ port (يعني
:3000أو:8080)
يعني http://example.com و https://example.com أصلهم اتنين Origin مختلفين رغم إن الدومين واحد، لأن الـ scheme اختلف. وكذلك http://localhost:3000 و http://localhost:4000 Origins مختلفين رغم إن الـ host واحد، لأن الـ port اختلف. التعريف ده موثّق في RFC 6454 ومُطبَّق نصاً في كل المتصفحات الحديثة.