🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

كيفية إصلاح طلب الاختبار المبدئي لـ CORS الذي فشل في الإنتاج

⏱️3 min read  ·  472 words

تعمل واجهة برمجة التطبيقات (API) الخاصة بك في مرحلة التطوير ولكنها تفشل في الإنتاج معطلب الاختبار المبدئي لـ CORS لا يجتاز فحص التحكم في الوصول أو يقوم طلب OPTIONS بإرجاع 404/405. تعد حالات فشل الاختبار المبدئي مشكلة CORS شائعة في الإنتاج فقط. وإليك كيفية تشخيصها وإصلاحها.

ما هو طلب الاختبار المبدئي؟

بالنسبة للطلبات “غير البسيطة” (PUT، أو DELETE، أو الرؤوس المخصصة، أو نوع محتوى JSON)، يرسل المتصفحالخيارات الطلب أولاً – “الاختبار المبدئي” – لسؤال الخادم عما إذا كان الطلب الحقيقي مسموحًا به. إذا فشل الاختبار المبدئي، فلن يتم تشغيل الطلب الفعلي أبدًا. غالبًا ما تظهر حالات فشل الاختبار المبدئي فقط في الإنتاج بسبب تكوينات الخادم المختلفة.

# Browser automatically sends this before your POST:
OPTIONS /api/users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

# Server MUST respond with matching CORS headers and 2xx status

السبب الأول: الخادم لا يتعامل مع الخيارات

// 🐛 Express route only handles POST, not OPTIONS preflight
app.post('/api/users', createUser);
// The OPTIONS preflight hits no handler → 404/405 → preflight fails

// ✅ Use the cors middleware which handles OPTIONS automatically
const cors = require('cors');
app.use(cors({
  origin: 'https://app.example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
}));
// cors() responds to preflight OPTIONS requests before your routes

السبب 2: عكس شرائط الوكيل أو خيارات الكتل

# nginx — explicitly handle OPTIONS preflight
location /api/ {
    # Handle preflight
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Max-Age' 86400 always;
        add_header 'Content-Length' 0;
        return 204;   # respond to preflight with 204 No Content
    }

    proxy_pass http://localhost:3000;
    add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
}

السبب 3: أصل حرف البدل مع بيانات الاعتماد

// 🐛 Wildcard origin CANNOT be used with credentials
app.use(cors({
  origin: '*',              // ❌ fails when credentials: true
  credentials: true,
}));
// Browser error: "Access-Control-Allow-Origin cannot be '*' when
// credentials mode is 'include'"

// ✅ Specify the exact origin
app.use(cors({
  origin: 'https://app.example.com',   // exact origin, not *
  credentials: true,
}));

// ✅ For multiple origins, echo the matching one
const allowed = ['https://app.example.com', 'https://admin.example.com'];
app.use(cors({
  origin: (origin, cb) => {
    if (!origin || allowed.includes(origin)) cb(null, true);
    else cb(new Error('Not allowed by CORS'));
  },
  credentials: true,
}));

السبب 4: الرؤوس المسموح بها مفقودة

// The preflight lists headers the real request will send.
// The server must allow ALL of them, or preflight fails.

// 🐛 Real request sends Authorization but server doesn't allow it
allowedHeaders: ['Content-Type'],   // missing Authorization

// ✅ Allow every custom header your client sends
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'X-API-Key'],

// Check the browser's preflight to see what's requested:
// Access-Control-Request-Headers: authorization, content-type
// The server's Access-Control-Allow-Headers must include all of these

السبب 5: تشغيل البرنامج الوسيط للمصادقة قبل CORS

// 🐛 Auth middleware rejects the OPTIONS preflight (no auth header on preflight!)
app.use(authenticate);   // blocks OPTIONS with 401
app.use(cors());

// ✅ CORS must come BEFORE auth so preflight passes
app.use(cors({ origin: 'https://app.example.com', credentials: true }));
app.use(authenticate);   // preflight already handled by cors()
// Preflight OPTIONS requests carry no credentials by design —
// they must not be blocked by auth

تصحيح أخطاء فشل الاختبار المبدئي

# Simulate the preflight with curl to see the server response
curl -X OPTIONS https://api.example.com/users \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: Content-Type, Authorization" \
  -i

# Check the response has:
# HTTP/1.1 204 (or 200)  ← must be 2xx
# Access-Control-Allow-Origin: https://app.example.com  ← matches
# Access-Control-Allow-Methods: ...POST...  ← includes your method
# Access-Control-Allow-Headers: ...content-type, authorization...

الأسئلة المتداولة

س: لماذا يعمل في التطوير ويفشل في الإنتاج؟
ج: غالبًا ما يستخدم التطوير وكيلًا (Vite/CRA) يتجنب CORS تمامًا، أو تكوين CORS المسموح به. يحتوي الإنتاج على وكيل عكسي حقيقي (nginx) وتكوين أكثر صرامة حيث يجب أن تكون معالجة الاختبار المبدئي صريحة.

س: لماذا يقوم طلب الخيارات الخاص بي بإرجاع 401؟
ج: يتم تشغيل البرنامج الوسيط للمصادقة قبل CORS ويرفض الاختبار المبدئي (الذي لا يحمل أي بيانات اعتماد حسب التصميم). ضع برنامج CORS الوسيط قبل المصادقة حتى تمر طلبات الاختبار المبدئي.

س: ما هو رمز الحالة الذي يجب أن يعود إليه الاختبار المبدئي؟
ج: 204 (بدون محتوى) أو 200. أي 2xx يعمل. الرقم 404 أو 405 أو 401 يعني فشل الاختبار المبدئي وأن المتصفح يحظر الطلب الحقيقي.

س: هل أقوم بتعيين رؤوس CORS في nginx أو التطبيق، وليس كليهما؟
ج: اختر مكانًا واحدًا. مكررAccess-Control-Allow-Origin تتسبب الرؤوس (من كل من nginx وExpress) في رفض المتصفح للاستجابة. استخدم nginx أو التطبيق، وليس كليهما.

س: كيف أسمح لبيانات الاعتماد (ملفات تعريف الارتباط) عبر الأصل؟
ج: مجموعةAccess-Control-Allow-Credentials: true وحدد أصلًا دقيقًا (وليس*) على الخادم، واستخدمcredentials: 'include' في جلب العميل. والثلاثة مطلوبة معاً.

الخلاصة

عادةً ما تأتي حالات فشل الاختبار المبدئي لـ CORS في الإنتاج من: الخادم الذي لا يتعامل مع الخيارات، أو الوكيل العكسي الذي يحظره، أو أصل حرف البدل مع بيانات الاعتماد، أو الرؤوس المسموح بها المفقودة، أو البرامج الوسيطة للمصادقة التي ترفض الاختبار المبدئي. الإصلاحات:استخدمcors البرامج الوسيطة (أو المعالجة الصريحة لخيارات nginx)، وحدد الأصول الدقيقة عند استخدام بيانات الاعتماد، واسمح لكل رأس يرسله عميلك، ووضع CORS قبل البرنامج الوسيط للمصادقة. تصحيح الأخطاء باستخدامcurl -X OPTIONS طلب معرفة ما يعرضه الخادم بالضبط – يجب أن يستجيب الاختبار المبدئي 2xx برؤوس CORS المطابقة.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা