تفتح F12 وتلقى في Console سطر أحمر طويل فيه has been blocked by CORS policy وفيه No 'Access-Control-Allow-Origin' header is present on the requested resource. معناه: المتصفح منع صفحتك من قراءة رد سيرفر على دومين ثاني، لأن هذاك السيرفر ما قال إنه يسمح لموقعك.
إيش هو CORS بكلام بسيط
المتصفح يحمي المستخدم: الصفحة اللي على دومين ما تقدر تقرأ ردود دومين ثاني إلا إذا الدومين الثاني سمح لها صراحة. السماح يكون بهيدر (header) في الرد اسمه Access-Control-Allow-Origin. إذا الهيدر ناقص، أو فيه دومين غير دومينك، المتصفح يرمي الرد ويطلع الخطأ.
أهم نقطة: CORS قاعدة في المتصفح، مو في السيرفر. لهذا نفس الطلب يشتغل من Postman أو من سيرفر، ويفشل من صفحتك. وقبل بعض الطلبات، مثل اللي ترسل JSON أو فيها هيدر Authorization، المتصفح يرسل طلب استئذان من نوع OPTIONS. إذا السيرفر ما رد عليه صح، الطلب الحقيقي ما ينرسل أصلًا.
ليش يصير كثير في المنتجات المبنية بالذكاء الاصطناعي
- الواجهة على دومين (Lovable أو Vercel أو Netlify) والـ API على دومين ثاني، والسيرفر ما انضبط يسمح لدومين الإنتاج. يشتغل في المعاينة لأن رابطها مسموح، ويتعطّل على دومينك.
- Supabase Edge Functions ما ترد على طلبات
OPTIONSلوحدها. لازم الكود يرد عليها، بنفسه أو عن طريقwithSupabase، ويضيف هيدرات CORS على كل رد، حتى ردود الخطأ. - الكود يحاول يكلّم خدمة خارجية مباشرة من المتصفح، مثل بوابة دفع أو API يحتاج مفتاح سري. كثير من هالخدمات ترفض الطلبات من المتصفح عن قصد، لأن مكانها السيرفر.
- السيرفر انهار بخطأ 500، ورد الخطأ طلع بدون هيدرات CORS. المتصفح يعرض لك خطأ CORS، والمشكلة الحقيقية خطأ في السيرفر.
كيف تتأكد بنفسك
- افتح F12، ثم تبويب Network، وسوّ الشي اللي يطلع الخطأ.
- دوّر على الطلب الأحمر. إذا قبله طلب
OPTIONSلنفس الرابط، شوف حالته: إذا 404 أو 401 أو 500، السيرفر ما يعرف يرد على الاستئذان. - افتح الطلب وشوف Response Headers: هل فيه
Access-Control-Allow-Origin؟ وإيش قيمته؟ لازم تكون دومينك بالضبط، معhttpsوبدون شرطة مائلة في الآخر. - شوف سجل السيرفر (Logs) في نفس اللحظة. إذا فيه خطأ 500، صلّح هذا أول، وغالبًا خطأ CORS يختفي معه.
سبب يفوت كثير: التحويل (redirect)
إذا رابط الـ API يحوّل لرابط ثاني، مثل من http إلى https، أو من رابط بدون شرطة في الآخر إلى رابط فيه شرطة، أو من الدومين بدون www إلى www، طلب الاستئذان OPTIONS ما يكمّل مع التحويل، والمتصفح يعتبره فشل CORS. في Network تشوف رد 301 أو 308 قبل الخطأ. الحل إن الكود يطلب الرابط النهائي مباشرة، بدون تحويل في النص.
ثلاث حلول غلط يقترحها الذكاء الاصطناعي
- إضافة متصفح (extension) تلغي CORS: تخلّيها تشتغل على جهازك فقط. عملاؤك ما عندهم الإضافة.
- `mode: 'no-cors'` في الكود: الخطأ يختفي، لكن الرد يرجع فاضي وما تقدر تقرأه. يعني بدّلت خطأ واضح بخطأ مخفي.
- السيرفر يسمح لأي دومين مع ملفات تعريف الارتباط (cookies): المتصفح يرفض النجمة
*لما الطلب يحمل بيانات الدخول، فيجي اقتراح أخطر: السيرفر يرجّع أي دومين يطلب منه، معAccess-Control-Allow-Credentials: true. هذا يعني إن أي موقع في الإنترنت يقدر يقرأ بيانات مستخدمك وهو مسجّل دخول عندك.
الحل الصح
- السيرفر يسمح لقائمة دومينات محددة: دومينك، و
wwwإذا تستخدمه، ورابط التجربة. غيرها لا. - يرد على
OPTIONSبنجاح، ومعهAccess-Control-Allow-MethodsوAccess-Control-Allow-Headersبالهيدرات اللي يرسلها كودك فعلًا، مثلauthorizationوcontent-type. - هيدرات CORS تنضاف لكل رد، حتى ردود الخطأ، عشان لما يصير خطأ تشوفه على حقيقته.
- أي خدمة تحتاج مفتاح سري تنطلب من سيرفرك، مو من المتصفح. شوف ليش في مفتاح OpenAI انكشف؟.
وإذا الخطأ اللي عندك Failed to fetch بدون سطر CORS، شوف TypeError: Failed to fetch.
تبيها تنحل صح؟
CORS تبان بسيطة، لكن الحل الغلط فيها يفتح ثغرة. في مكالمة استشارية نوصل للسبب على شاشتك ونضبط القائمة الصح. وإذا المشكلة جزء من أشياء كثيرة متعطّلة، الإصلاح المباشر يصلّحها كلها على نسخة منفصلة من منتجك.