إعادة التوجيه في HTTP

إعادة التوجيه في HTTP (Redirect) استجابةٌ برمز حالة من الفئة 3xx ومعها ترويسة Location تحمل عنواناً آخر. ويتبعها المتصفح من تلقاء نفسه، فيرسل طلباً جديداً إلى ذلك العنوان ولا يعرض إلا الصفحة الأخيرة. فمثلاً قد يعيد الطلب GET http://shop.example.com/sale الاستجابة 301 Moved Permanently مع Location: https://shop.example.com/offers، فيطلب المتصفح ذلك العنوان ويتلقى 200 OK.

خمسة رموز تؤدي العمل اليومي: 301 و302 و303 و307 و308. وتختلف في الإجابة عن سؤالين: هل الانتقال دائم؟ وهل يجوز للعميل أن يحوّل POST إلى GET عندما يتبع إعادة التوجيه؟

لماذا يهم

تقع إعادة التوجيه على مسارات حرجة: الانتقال من HTTP إلى HTTPS، والروابط القديمة بعد إعادة التصميم، وتسجيل الدخول والخروج، والعودة من الدفع، وتغيير إصدار الواجهة البرمجية. وعندما تخطئ تكون الأعراض محيّرة: بيانات نموذج تضيع لأن POST صار GET، وصفحة تعيد التوجيه حتى يستسلم المتصفح، وروابط قديمة تنتهي عند 404، أو إعادة توجيه دائمة خاطئة يظل المتصفح يتبعها بعد الإصلاح لأنه خزّنها مؤقتاً.

وإعادة التوجيه موضوع أمني أيضاً: فالتطبيق الذي يعيد التوجيه إلى أي عنوان يحمله أحد المعاملات يتيح لأي شخص أن يبني رابطاً على نطاقك الموثوق ينتهي بصفحة تصيّد احتيالي (Phishing). ولأن المتصفح يتبع إعادة التوجيه بصمت، تبقى هذه العيوب خفية ما لم ينظر المختبِر إلى كل قفزة.

خمسة رموز وسؤالان

يعرّف المعيار RFC 9110 رموز إعادة التوجيه. فالدائم (301 و308) يعني أن للمورد عنواناً دائماً جديداً ينبغي أن تستخدمه كل الإشارات اللاحقة إليه: فتحدّث محركات البحث وبرامج الزحف الأخرى روابطها إليه، ويجوز لذاكرات التخزين المؤقت أن تخزّن إعادة التوجيه حتى دون ترويسات تخزين مؤقت. والمؤقت (302 و307) يعني أن العنوان الأصلي يبقى صالحاً. أما 303 فليس هذا ولا ذاك: إنه يشير إلى مورد آخر يحمل جواب هذا الطلب. ولا تُخزَّن الرموز المؤقتة والرمز 303 مؤقتاً إلا إذا سمحت بذلك ترويسات التخزين المؤقت.

وفي ما يخص الطريقة، يذكر RFC 9110 أنه لأسباب تاريخية يجوز للعميل أن يغيّر POST إلى GET عندما يتبع 301 أو 302، والمتصفحات تفعل ذلك وفق معيار Fetch، فتُسقط جسم الطلب. ويمنع الرمزان 307 و308 ذلك: يجب على العميل أن يكرر الطريقة نفسها بالجسم نفسه.

  • 301 Moved Permanently: دائم؛ وقد يصير POST طلب GET. للصفحات التي انتقلت نهائياً.
  • 302 Found: مؤقت؛ وقد يصير POST طلب GET. لتحويلة قصيرة، مثل التحويل إلى صفحة تسجيل الدخول.
  • 303 See Other: الطلب التالي GET (ويبقى HEAD طلب HEAD). لصفحة النتيجة بعد إرسال نموذج بطريقة POST (نمط Post/Redirect/Get)، حتى لا يعيد تحديث الصفحة إرسال النموذج.
  • 307 Temporary Redirect: مؤقت؛ وتبقى الطريقة والجسم كما هما. لانتقال مؤقت لنقطة نهاية تستقبل POST.
  • 308 Permanent Redirect: دائم؛ وتبقى الطريقة والجسم كما هما. لنقطة نهاية في واجهة برمجية انتقلت نهائياً.
  • 304 Not Modified من الفئة 3xx أيضاً، لكنه لا يرسل العميل إلى عنوان جديد: لا يحمل Location، بل يطلب من العميل أن يعيد استخدام نسخته المخزّنة بعد طلب مشروط (ويسمّيه RFC 9110 إعادة توجيه إلى نتيجة مخزّنة سابقاً)، كما في درس التخزين المؤقت.
من رموز إعادة التوجيه الخمسة، 301 و308 دائمان وقابلان للتخزين المؤقت افتراضياً، ولا يضمن إلا 307 و308 أن يُرسَل طلب POST المُعاد توجيهه مرة أخرى بطريقة POST ومعه جسمه.
الرمزدائم أم مؤقتالطلب التالي بعد POSTقابل للتخزين المؤقت افتراضياًدون ترويسات تخزين مؤقتالاستخدام المعتاد
301Moved Permanentlyدائمقد يصير GET ويُسقَط الجسمنعمصفحة انتقلت نهائياً
302Foundمؤقتقد يصير GET ويُسقَط الجسملاتحويلة قصيرة، مثل صفحة تسجيل الدخول
303See Otherلا هذا ولا ذاك: يشير إلى الجوابGET دائماً ويُسقَط الجسملاصفحة النتيجة بعد إرسال نموذج POST
307Temporary RedirectمؤقتPOST مجدداً بالجسم نفسهلاانتقال مؤقت لنقطة نهاية تستقبل POST
308Permanent RedirectدائمPOST مجدداً بالجسم نفسهنعمنقطة نهاية API انتقلت نهائياً
  • الطريقة والجسم محفوظان

جرّبها: مرجع رموز حالة HTTP

مثال تطبيقي: طلب شراء ضاع في إعادة توجيه

تعرّفت إلى هذا العيب في الصف R8 من تمرين رموز حالة HTTP: اتبع التطبيق إعادة التوجيه 301 بطلب GET، وانتهت السلسلة باستجابة 200 مضلِّلة. وإليك كيف يظهر في المتصفح، قفزةً بعد قفزة. ينقل متجر إلكتروني واجهة API الخاصة بالطلبات من /api/v1/orders إلى /api/v2/orders، ويضيف 301 من المسار القديم من أجل الصفحات التي ما زالت مفتوحة في متصفحات العملاء. ثم يضغط هؤلاء العملاء «إتمام الطلب» فيرون «حدث خطأ ما». وفي أدوات المطوّرين (DevTools)، مع تفعيل «Preserve log»، تُظهر لوحة الشبكة (Network) طلبين لنقرة واحدة (السجل في آخر هذا القسم).

اتبع المتصفح إعادة التوجيه 301 كما يقتضي معيار Fetch لطلب POST: أرسل GET بلا جسم، ولم ترَ الصفحة إلا الاستجابة الأخيرة 405. يذكر تقرير الخطأ القفزتين ويسمّي السبب المرجّح: 301 بدلاً من 308. وبعد الإصلاح يتحقق اختبار التأكيد من أن POST يصل إلى v2 بجسمه، ويعيد 201 Created، وينشئ طلباً واحداً بالضبط.

وخارج المتصفح يُظهر الأمر curl -i استجابة إعادة التوجيه نفسها، لأن curl لا يتبع إعادة التوجيه إلا مع -L؛ وتعرض أداة Redirect Checker كل قفزة لعنوان عام، وتطلب كل قفزة بطريقة GET. وفي كل إعادة توجيه تحقّق مما يلي:

  • الحالة: الرمز الذي يحتاجه المتطلب بالضبط، لا أي رمز من الفئة 3xx.
  • Location: الوجهة الصحيحة، على https، مع الاحتفاظ بالمسار وسلسلة الاستعلام (Query String) حين يلزم (/old?id=7 ← /new?id=7).
  • القفزات: أقل عدد ممكن، والأفضل قفزة واحدة.
  • النهاية: الصفحة أو المورد المتوقع، لا 404 ولا صفحة تسجيل الدخول.
  • الطريقة والجسم: ما الذي وصل فعلاً بعد إعادة توجيه طلب POST.
--- request 1 (the click) ---
POST /api/v1/orders HTTP/1.1
Host: shop.example.com
Content-Type: application/json

{"sku":"A-100","qty":2}

HTTP/1.1 301 Moved Permanently
Location: /api/v2/orders

--- request 2 (sent by the browser: method changed, body dropped) ---
GET /api/v2/orders HTTP/1.1
Host: shop.example.com

HTTP/1.1 405 Method Not Allowed
Allow: POST

جرّبها: مولّد أوامر cURL جرّبها: محلّل ترويسات HTTP

سلاسل إعادة التوجيه وحلقاتها

سلسلة إعادة التوجيه (Redirect Chain) عدة عمليات إعادة توجيه متتالية: http://example.com/shoes ← https://example.com/shoes ← https://www.example.com/shoes ← https://www.example.com/shoes/. وكل قفزة رحلة ذهاب وإياب إضافية، وقاعدة أخرى قد تنكسر. وتنمو السلاسل طبقة فوق طبقة: موازن الأحمال يحوّل إلى HTTPS، وخادم الويب يضيف www، والتطبيق يضيف الشرطة المائلة الختامية. والحل أن يُرسَل كل عنوان قديم مباشرةً إلى عنوانه النهائي.

حلقة إعادة التوجيه (Redirect Loop) سلسلة لا تنتهي. وتستسلم المتصفحات (يتوقف معيار Fetch عند 20 إعادة توجيه) وتعرض خطأ مثل ERR_TOO_MANY_REDIRECTS في Chrome. وتنشأ الحلقات من مكوّنين لا يتفقان:

  • وكيل (Proxy) أو موازن أحمال يستقبل الطلب عبر HTTPS لكنه يمرّره إلى التطبيق عبر HTTP العادي؛ والتطبيق، الذي يفرض HTTPS هو أيضاً، يرى HTTP فيعيد التوجيه إلى عنوان https نفسه من جديد، في كل طلب.
  • خادم الويب يضيف الشرطة المائلة الختامية والتطبيق يحذفها.
  • صفحة تسجيل الدخول ترسل المستخدم المسجَّل إلى لوحة التحكم، ولوحة التحكم تعيد من تعدّه غير مسجَّل إلى صفحة الدخول، مثلاً لأن ملف تعريف الارتباط الخاص بالجلسة ضُبط لنطاق أو مسار آخر.

جرّبها: فاحص إعادة التوجيه

خطر إعادة التوجيه المفتوحة

تعيد تطبيقات كثيرة التوجيه إلى عنوان مأخوذ من الطلب: بعد تسجيل الدخول يعيد ?next=/orders/15 المستخدم إلى الصفحة التي أرادها. فإذا قُبلت أي قيمة، أرسل https://shop.example.com/login?next=https://example.org/ المستخدمين من نطاقك الموثوق إلى أي موقع. هذه إعادة توجيه مفتوحة (Open Redirect)، وتُسمّى أيضاً إعادة توجيه غير متحقَّق منها (Unvalidated Redirect)، ويستغلها المهاجمون في التصيّد الاحتيالي لأن الرابط يبدأ بنطاقك الحقيقي.

توصي ورقة OWASP الإرشادية الخاصة بإعادة التوجيه غير المتحقَّق منها بتجنّب الوجهات التي يقدّمها المستخدم، أو ربط معرّف قصير بعنوان محفوظ على الخادم، أو مطابقة الوجهة مع قائمة سماح (Allow-list). لا تختبر إلا أنظمة تملكها أو لديك إذن مكتوب باختبارها، في بيئة الاختبار، مع النطاق المحجوز example.org وجهةً خارجية. جرّب كل قيمة مما يلي في المعامل؛ والنتيجة المتوقعة دائماً صفحة من موقعك أو وجهة افتراضية آمنة.

  • عنوان مطلق: https://example.org/.
  • عنوان نسبي للبروتوكول، يعدّه المتصفح خارجياً: //example.org/.
  • عنوان مشابه يبدأ بنطاقك: https://shop.example.com.example.org/.
  • نطاقك بوصفه معلومات مستخدم قبل @، ويظل يقود إلى example.org: https://shop.example.com@example.org/.
  • أماكن أخرى: معاملات مثل returnUrl أو redirect، وصفحات تسجيل الخروج والعودة من الدفع.

جرّبها: محلّل عناوين URL

أخطاء شائعة

  • التحقق من الصفحة النهائية في شريط العنوان فقط، دون النظر إلى القفزات والرموز وقيم Location بينها.
  • قبول أي رمز 3xx على أنه ناجح: فالاختيار بين 301 و302 يحدد هل الانتقال دائم، وبين 302 و307 يحدد هل يحتفظ POST بجسمه.
  • إعادة اختبار إعادة توجيه دائمة أُصلحت في متصفح خزّن النسخة القديمة. استخدم ملف تعريف (Profile) نظيفاً في المتصفح أو curl.
  • الاختبار بصفة زائر جديد فقط، مع أن الحلقات كثيراً ما لا تظهر إلا مع جلسة قائمة أو ملف تعريف ارتباط للغة أو عنوان بداية معيّن.
  • الاستهانة بإعادة التوجيه المفتوحة، أو تجربتها على مواقع حقيقية لأطراف ثالثة بدلاً من نطاق محجوز.

من منظور ضمان الجودة

تنتمي إعادة التوجيه إلى كل تغيير ينقل عنواناً: إعادة تصميم، أو إصدار جديد للواجهة البرمجية، أو انتقال نطاق أو انتقال إلى HTTPS. اطلب خريطة إعادة التوجيه (العنوان القديم، والعنوان الجديد، ورمز الحالة، وهل تُحفظ سلسلة الاستعلام)، وحوّلها إلى اختبار مبني على البيانات بصف لكل عنوان قديم. وشغّله مجدداً بعد النشر، لأن قواعد شبكة توصيل المحتوى (CDN) والخادم كثيراً ما تختلف بين البيئات.

وفي تقرير خطأ إعادة التوجيه، أدرج كل قفزة بطريقتها وعنوانها وحالتها وLocation الخاصة بها، واذكر القاعدة التي تشير إليها القفزات بوصفها فرضية يؤكدها الفريق. وأبلغ عن إعادة التوجيه المفتوحة عبر الإجراء الذي يتّبعه فريقك للعيوب الأمنية.

تمرين عملي

راجع ثلاثة سجلات لإعادة التوجيه

المطلوب

لعملية ترحيل موقع متطلبان. R1: يصل كل عنوان قديم إلى صفحته الجديدة بإعادة توجيه دائمة واحدة، على HTTPS، مع الاحتفاظ بسلسلة الاستعلام. R2: بعد تسجيل الدخول يعود المستخدم إلى الصفحة المذكورة في ?next=، ويجب أن تكون من هذا الموقع. والسجل C أدناه تسجيل دخول ناجح بحساب اختبار.

قرّر هل يستوفي كل سجل المتطلبات؛ ولكل سجل فاشل اكتب السجل المتوقع وعنواناً لتقرير الخطأ في سطر واحد.

المادة
Trace A   GET http://example.com/sale?src=mail
  301     Location: https://example.com/sale?src=mail
  301     Location: https://www.example.com/sale?src=mail
  302     Location: https://www.example.com/offers
  200     OK

Trace B   GET https://www.example.com/help
  301     Location: https://www.example.com/help/
  301     Location: https://www.example.com/help
  301     Location: https://www.example.com/help/
  ...     ERR_TOO_MANY_REDIRECTS

Trace C   POST https://www.example.com/login?next=https://example.org/
  303     Location: https://example.org/
الخطوات
  1. عُدّ القفزات في كل سجل.
  2. طابق كل رمز حالة مع R1 وR2.
  3. افحص كل Location: البروتوكول والمضيف والمسار وسلسلة الاستعلام.
  4. اكتب الحكم والسجل المتوقع وعنوان تقرير الخطأ.
إظهار الإجابة النموذجية
الإجابة النموذجية

السجل A لا يستوفي R1 من ثلاث جهات: ثلاث قفزات بدلاً من واحدة، و302 مؤقتة في النهاية، وضياع سلسلة الاستعلام src=mail في تلك القفزة. المتوقع: http://example.com/sale?src=mail ثم 301 إلى https://www.example.com/offers?src=mail ثم 200. العنوان: «العنوان القديم /sale: ثلاث عمليات إعادة توجيه، و302 في النهاية، وسلسلة استعلام مفقودة».

السجل B حلقة: قاعدة تضيف الشرطة المائلة الختامية وأخرى تحذفها، فلا يمكن الوصول إلى صفحة المساعدة. المتوقع: صيغة قياسية واحدة يُوصل إليها بإعادة توجيه 301 واحدة على الأكثر. العنوان: «حلقة إعادة توجيه في /help: الشرطة المائلة الختامية تُضاف وتُحذف بالتناوب».

السجل C لا يستوفي R2: إنه إعادة توجيه مفتوحة. والرمز 303 بعد طلب POST لتسجيل الدخول صحيح؛ أما العيب ففي الوجهة. المتوقع: رفض القيمة الخارجية ووصول المستخدم إلى صفحة افتراضية مثل /account. وأبلغ عنه بوصفه عيباً أمنياً.

أسئلة شائعة

ما الفرق بين 302 و307؟

كلاهما مؤقت. عند اتباع 302 يجوز للعميل أن يحوّل POST إلى GET، والمتصفحات تفعل ذلك؛ أما مع 307 فيجب أن يكرر الطريقة نفسها والجسم نفسه. وفي طلب GET يتصرف الرمزان بالطريقة نفسها.

هل أستخدم 301 أم 308؟

للصفحات التي لا تُطلب إلا بطريقة GET يصلح كلاهما، و301 هو الخيار المعتاد منذ زمن طويل. أما نقطة النهاية في واجهة برمجية أو النموذج الذي يستقبل POST فاستخدم له 308 حتى تبقى الطريقة والجسم كما هما.

لماذا تعرض أدوات المطوّرين في Chrome الرمز 307 Internal Redirect بينما يرسل خادمي 301؟

لأن المتصفح أعاد توجيه الطلب بنفسه قبل إرساله، مثلاً لأن الموقع يستخدم HSTS فيرقّي المتصفح http إلى https تلقائياً. هذا الصف ليس استجابة من الخادم. ولمعرفة ما يعيده الخادم فعلاً استخدم curl أو Redirect Checker.

المراجع

  • RFC 9110: HTTP SemanticsIETF · 2022معاني أساليب HTTP وحقول الترويسات ورموز الحالة والتفاوض على المحتوى، بغضّ النظر عن إصدار البروتوكول.
  • Fetch StandardWHATWGيعرّف CORS: طلبات التحقق المسبق وترويسات Access-Control-* والاستجابات التي يسمح المتصفح للصفحة برؤيتها.
  • OWASP Cheat Sheet Series: Unvalidated Redirects and ForwardsOWASP Foundationإعادة التوجيه المفتوحة: لماذا يجب ألا تأتي وجهة إعادة التوجيه من مدخلات المستخدم، والدفاعات — ربط المعرّفات بالروابط في الخادم، وقوائم السماح، وصفحات التأكيد.
  • Redirections in HTTP — MDNMDN Web Docs (Mozilla)إعادة التوجيه في HTTP: رموز الحالة 3xx مع ترويسة Location، والدائمة (301 و308) مقابل المؤقتة (302 و303 و307)، وكيف تنشأ حلقات إعادة التوجيه.

تدرّب بأدوات متقن

تتبّع سلاسل إعادة التوجيه (301/302/307/308) وعرض كل قفزة وحالتها وعنوان URL النهائي.

الويب

ابحث عن أي رمز حالة HTTP لمعرفة معناه ومرجعه في RFC ومتى يُستخدم.

الويب

إنشاء أوامر cURL من الطريقة وعنوان URL والترويسات والمتن وخيارات المصادقة.

الويب

تحليل ترويسات طلبات واستجابات HTTP الخام إلى جدول منظّم مع الشروحات.

الويب

تفكيك عنوان URL إلى البروتوكول والمضيف والمنفذ والمسار ومعاملات الاستعلام والجزء (fragment) وبيانات الاعتماد.

الويب

للتعمّق أكثر

تقدّمك في هذا الموضوع

جزء من مسار تعلم ضمان الجودة