في هذا الدرس 7تصفّح المواضيع
تخيّل أنك تطلب منتجاً من متجر إلكتروني: أنت تملأ استمارة طلب وترسلها للمتجر، فيقرأ المتجر استمارتك ويرسل لك طرداً فيه المنتج. في عالم الويب، المتصفح والسيرفر يتكلمان معاً بنفس الطريقة بالضبط عبر رسائل HTTP (HTTP Messages)؛ فهي الرسائل المنظمة التي يرسلها المتصفح ليطلب صفحة أو يرسل بيانات، ويرد بها السيرفر ليعرض لك النتيجة.
في الإصدار الأساسي للويب (HTTP/1.1)، تُكتب هذه الرسالة كنص عادي مقروء سطراً بسطر يمكن لأي شخص قراءته وفهمه بالعين المجردة، كما يظهر في المثال البسيط الآتي لطلب صفحة من الموقع وردّ السيرفر عليها:
# 1. HTTP Request Message (Client to Server)
GET /index.html HTTP/1.1
Host: www.closetag.com
Accept: text/html
# 2. HTTP Response Message (Server to Client)
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
<h1>Welcome to CloseTag</h1>
في تصفحك اليومي، أنت لا تكتب هذه الأسطر بيدك؛ فالمتصفح يجهز رسالة الطلب ويرسلها تلقائياً بمجرد ضغطك على أي رابط، والسيرفر يستقبلها ويرسل لك رسالة الرد في أجزاء من الثانية.
لماذا نتعلم قراءة هذه الرسائل إذا كان المتصفح يكتبها عنا؟
- في تطوير واختبار التطبيقات: عندما تبني موقعاً أو تطبيقاً ولا تعمل صفحة معينة أو لا تصل بيانات المستخدم للسيرفر، فإن فتح الرسالة وقراءتها يخبرك فوراً أين المشكلة بالضبط.
- في حماية الأنظمة وفحص الشبكة: فحص الرسائل الخارجة والواردة يكشف لك هل تُرسل البيانات الحساسة (مثل كلمات المرور) بشكل مكشوف أم محمي، وهل يحاول شخص التلاعب بالطلب قبل وصوله للسيرفر.
كل المحادثات التي تدور على الويب تنقسم إلى نوعين اثنين فقط من الرسائل لا ثالث لهما: رسالة الطلب (Request Message) ورسالة الاستجابة (Response Message). تشبه العملية حواراً بسيطاً: سؤال يخرج من متصفحك، وجواب يعود من السيرفر.
النوع الأول: رسالة الطلب (): هي الرسالة التي يبدأ بها العميل (المتصفح) ليطلب صفحة أو يرسل بيانات للسيرفر. ويبدأ أول سطر فيها دائماً بثلاث كلمات واضحة: ماذا نريد أن نفعل؟ (مثل GET لجلب صفحة)، وأين تقع الصفحة؟ (المسار)، وأي إصدار نستخدم؟ (HTTP/1.1) كما في المثال الآتي:
# 1. HTTP Request (Client to Server)
GET /index.html HTTP/1.1
Host: www.closetag.com
User-Agent: Mozilla/5.0
Accept: text/html
النوع الثاني: رسالة الاستجابة (): هي الرسالة التي يرد بها السيرفر على طلب المتصفح، وتحمل الصفحة المطلوبة إذا نجح الطلب، أو سبب المشكلة إذا لم يجد الصفحة. ويبدأ أول سطر فيها بثلاثة عناصر: إصدار البروتوكول (HTTP/1.1)، ثم رقم النتيجة (مثل 200 للنجاح)، ثم كلمة توضيحية للنتيجة (مثل OK) كما يظهر هنا:
# 2. HTTP Response (Server to Client)
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 43
<html><body><h1>CloseTag</h1></body></html>
تتشابه رسالتا الطلب والاستجابة في شكل القالب العام، وتختلفان في ثلاث نقاط بسيطة:
- من يرسل لمن؟ (الاتجاه): الطلب يخرج من العميل إلى السيرفر، والاستجابة تعود من السيرفر إلى العميل.
- ما الهدف؟ (الوظيفة): الطلب يسأل عن صفحة أو يرسل بيانات، والاستجابة تعيد النتيجة والمحتوى المطلوب.
- السطر الأول: الطلب يبدأ بالفعل والمسار (مثل
GET /tutorials)، بينما الاستجابة تبدأ برقم النتيجة وحالتها (مثل200 OK) كما يوضح الرسم الآتي:
عندما تفتح موقعاً وتظهر لك شاشة بيضاء أو رسالة خطأ عامة، فإن شاشة المتصفح العادية لا تخبرك بالسبب الحقيقي. هنا تظهر أهمية قراءة رسائل HTTP مباشرة؛ لأنها الصندوق الأسود الذي يسجل كل ما قاله المتصفح للسيرفر وكل ما ردّ به السيرفر بالحرف الواحد:
- معرفة مكان العطل فوراً (للمبرمج): هل المشكلة في الطلب الذي أرسله المتصفح (مثلاً عنوان مكتوب خطأ أو بيانات ناقصة)، أم أن الطلب وصل سليم والسيرفر نفسه تعطل أثناء معالجته؟ قراءة رسالة الاستجابة تحسم هذا السؤال في ثانية واحدة.
- التأكد من وصول البيانات بشكل صحيح: عندما يملأ المستخدم نموذج تسجيل أو طلب شراء، تتيح لك قراءة رسالة الطلب التأكد من أن الاسم والسعر وصلا للسيرفر بالتنسيق الصحيح دون نقص.
- تسريع تحميل الموقع: من خلال قراءة الرسائل، يفهم المطور حجم الملفات المرسلة وهل يقوم المتصفح بحفظ نسخة منها لتسريع الفتح في المرات القادمة أم يعيد تحميلها كل مرة.
- حماية بيانات المستخدمين (للأمن السيبراني): يقرأ مختص الأمن رسائل HTTP ليتأكد أن بيانات تسجيل الدخول لا تسير في الشبكة كنص مكشوف، وليكتشف أي محاولة لتزوير الطلبات أو التلاعب بها قبل وصولها للسيرفر.
تشبه أي رسالة HTTP طرداً بريدياً منظماً يتكون من أربعة أقسام مرتبة بالترتيب نفسه دائماً (سواء كانت رسالة طلب أو رسالة استجابة):
- سطر البداية (Start-Line — إلزامي): هو أول سطر في أعلى الرسالة، ويحدد نوع الرسالة وهدفها باختصار.
- رؤوس الرسالة (Headers — صفر أو أكثر): تشبه البطاقة الملصقة على الطرد من الخارج؛ وهي أسطر تحمل معلومات إضافية عن الرسالة (مثل اسم الموقع ونوع المحتوى وحجمه).
- السطر الفارغ الفاصل (Blank Line — إلزامي): سطر فارغ تماماً يفصل بين البطاقة الخارجية (الرؤوس) وبين البضاعة الموجودة داخل الطرد.
- جسم الرسالة (Message Body — اختياري): هو المحتوى الفعلي داخل الطرد (مثل كود الصفحة أو بيانات المستخدم)، ويأتي دائماً بعد السطر الفارغ، وقد يكون فارغاً إذا لم نكن بحاجة لإرسال محتوى.
يُطلق على القسمين الأول والثاني معاً (سطر البداية + رؤوس الرسالة) اسم مقدمة/رأس الرسالة (Message Head)، بينما يمثل القسم الرابع جسم الرسالة (Message Body) كما يظهر في القالب التوضيحي الآتي:
# RFC 9112 Generic HTTP Message Syntax (ABNF)
HTTP-message = start-line CRLF
*( field-line CRLF )
CRLF
[ message-body ]
لاحظ كيف يُكتب كل قسم من الأقسام الأربعة في الواقع العملي:
- في سطر البداية: نكتب في الطلب (الفعل + المسار + الإصدار)، ونكتب في الاستجابة (الإصدار + رقم الحالة + كلمة التوضيح).
- في رؤوس الرسالة: نكتب كل رأس في سطر مستقل بصيغة
الاسم: القيمة(مثلHostلاسم الموقع، وContent-Typeلنوع المحتوى، وContent-Lengthلطول الجسم بالبايتات). ولا يهم إن كتبت اسم الرأس بحروف كبيرة أو صغيرة. - في السطر الفارغ وجسم الرسالة: نترك سطراً فارغاً تماماً (
CRLF)، ثم نضع محتوى الجسم إن وُجد.
قارن في المثالين الآتيين بين طلب بسيط لجلب صفحة (بدون جسم)، وطلب إرسال بيانات يحتوي على جسم طوله 25 بايت بالضبط:
# A) GET Request (Start-Line + Headers + Blank Line, No Body)
GET /index.html HTTP/1.1
Host: www.closetag.com
Accept: text/html
# B) POST Request (All 4 Sections Including JSON Message Body)
POST /api/users HTTP/1.1
Host: www.closetag.com
Content-Type: application/json
Content-Length: 25
{"username": "ahmed_ali"}
وبالمثل، تنطبق الأقسام الأربعة القياسية ذاتها بالترتيب نفسه على رسالة الاستجابة العائدة من الخادم إلى العميل. إذ يتصدر سطر الحالة مطلع الرسالة، تليه حقول رؤوس الاستجابة مثل Content-Type و Content-Length، ثم السطر الفارغ الفاصل CRLF. ويأتي أخيراً جسم الاستجابة الحامل لمستند HTML كما يظهر في المثال والمخطط التشريحي أدناه:
# HTTP Response (Start-Line + Headers + Blank Line + 137-Byte Body)
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 137
<!DOCTYPE html>
<html>
<head><title>CloseTag</title></head>
<body>
<h1>Welcome to CloseTag</h1>
<p>HTTP</p>
</body>
</html>
مثال كامل متصل: طلب HTTP حقيقي والاستجابة المقابلة له سطراً بسطر
لمعاينة كيف تترابط الأقسام الأربعة في دورة اتصال حقيقية كاملة، تأمل السيناريو المتصل الآتي: يرسل العميل طلب إنشاء طلبية جديدة يحمل جسماً بصيغة JSON طوله 30 بايت بالضبط، فيعالجه الخادم ويعيد استجابة مقابلة برمز الإنشاء 201 Created تحمل جسماً طوله 40 بايت:
# === [1] OUTBOUND HTTP REQUEST (Client -> Server) ===
POST /api/v1/orders HTTP/1.1
Host: store.closetag.com
User-Agent: CloseTagClient/2.0
Content-Type: application/json
Accept: application/json
Content-Length: 30
{"item": "Keyboard", "qty": 1}
# === [2] MATCHING HTTP RESPONSE (Server -> Client) ===
HTTP/1.1 201 Created
Date: Mon, 02 Nov 2026 12:30:00 GMT
Server: nginx
Location: /api/v1/orders/904
Content-Type: application/json
Content-Length: 40
{"order_id": 904, "status": "confirmed"}
يُفصّل الجدول التشريحي الآتي وظيفة كل سطر في هذه الدورة المتصلة وكيف استجابت رؤوس الخادم لمعطيات الطلب:
| الرسالة والقسم | السطر المقابل في الكود | الوظيفة التشريحية وعلاقة الطلب بالاستجابة |
|---|---|---|
| الطلب — سطر البداية | السطر الأول (الفعل POST ومسار الواجهة) |
يحدد فعل الإنشاء POST، ومسار الواجهة بصيغة الأصل origin-form، وإصدار البروتوكول HTTP/1.1. |
| الطلب — رأس المضيف | رأس Host للنطاق المستهدف |
رأس إلزامي في HTTP/1.1 تحدد النطاق المستهدف store.closetag.com على الخادم. |
| الطلب — التفاوض على المحتوى | رأسا Content-Type و Accept |
تُخبر الخادم أن جسم الطلب المرسل هو application/json، وتطلب منه أن يعيد الرد بالصيغة نفسها. |
| الطلب — رأس طول الجسم | رأس Content-Length بقيمة 30 |
تُلزم محلل الخادم بقراءة 30 بايت بالضبط فور انتهاء السطر الفارغ. |
| الطلب — السطر الفارغ والجسم | الفاصل CRLF ثم كائن JSON |
يفصل السطر الفارغ الرأس عن الجسم، وتنتقل البايتات الثلاثون إلى تطبيق الخادم لإنشاء الطلبية. |
| الاستجابة — سطر الحالة | سطر الحالة برمز 201 Created |
يرد الخادم برمز 201 Created تأكيداً لنجاح فعل POST وإنشاء مورد جديد فعلياً. |
| الاستجابة — رأس الموقع | رأس Location لمعرّف الطلبية 904 |
تقترن برمز 201 لتزويد العميل بالمسار الدقيق للطلبية الجديدة التي أُنشئت برقم 904. |
| الاستجابة — رؤوس المحتوى | رأس Content-Type ورأس الطول بقيمة 40 |
يلبي الخادم طلب العميل في Accept فيعيد النتيجة بصيغة application/json ويعلن أن حجمها 40 بايت. |
| الاستجابة — السطر الفارغ والجسم | الفاصل CRLF ثم كائن التأكيد |
بعد السطر الفارغ الفاصل، يرسل الخادم كائن التأكيد المكون من 40 بايت ليقرأه العميل ويغلق الدورة بنجاح. |
مختبر تفاعلي: ابنِ رسالة HTTP واضغط على أي سطر فيها لمعرفة وظيفته التشريحية
استخدم الأداة التفاعلية أدناه لتجربة تغيير فعل الطلب والمسار والرؤوس وكتابة نصوص عربية أو إنجليزية داخل جسم الرسالة، وانقر على أي سطر في المعاينة اليسرى ليظهر لك شرح وظيفته الدقيقة. ولاحظ كيف يظهر محرفا \r\n في نهاية كل سطر في HTTP/1.1، وكيف يُحسب طول Content-Length بالبايت تلقائياً بترميز UTF-8، وكيف يعرض المختبر محاكاة منطقية مبسّطة لتحول أقسام الرسالة إلى إطاري HEADERS و DATA في HTTP/2 و HTTP/3 (علماً أن الإطارات الفعلية على السلك تكون ثنائية ومضغوطة):
تفصيل تقني متقدم: أشكال هدف الطلب الأربعة (Request-Target Forms) من البسيط إلى المتقدم (RFC 9112 §3.2)
في الأمثلة السابقة رأينا الشكل القياسي اليومي لهدف الطلب داخل سطر البداية. ولتغطية جميع حالات الاتصال المباشر أو عبر الوسطاء، يحدّد المعيار RFC 9112 (القسم 3.2) أربعة أشكال قياسية لهدف الطلب (request-target):
- صيغة المسار الأصلي (origin-form): المسار النسبي متبوعاً باستعلام اختياري؛ وهي الصيغة القياسية الأكثر شيوعاً عند مخاطبة الخادم الأصلي مباشرةً مع رأس
Host. - الصيغة المطلقة الكاملة (absolute-form): عنوان المورد الكامل شاملاً المخطط والمضيف؛ وتُستخدم في
HTTP/1.1عند إرسال الطلب عبر خادم وسيط أمامي (Forward Proxy). - صيغة السلطة (authority-form): اسم المضيف ورقم المنفذ فقط؛ وتُستخدم حصراً مع الفعل
CONNECTلطلب إنشاء نفق اتصال مباشر (TCP Tunnel) عبر الخادم الوسيط (لا يشفّرCONNECTالبيانات بذاته، لكنه يُستخدم غالباً لتمرير اتصالاتTLS / HTTPSالمشفرة عبر ذلك النفق). - صيغة النجمة العامة (asterisk-form): رمز النجمة المفردة
*؛ وتُستخدم مع الفعلOPTIONSللاستعلام عن قدرات الخادم ككل بدلاً من مورد محدد.
# 1. origin-form (Standard request to an origin server)
GET /tutorials/http?page=1 HTTP/1.1
Host: www.closetag.com
# 2. absolute-form (Request sent via a forward proxy)
GET http://www.closetag.com/tutorials/http HTTP/1.1
Host: www.closetag.com
# 3. authority-form (Used only with CONNECT to open a TCP tunnel)
CONNECT www.closetag.com:443 HTTP/1.1
Host: www.closetag.com:443
# 4. asterisk-form (Server-wide OPTIONS capability check)
OPTIONS * HTTP/1.1
Host: www.closetag.com
تخيّل أنك تكتب قائمة مشتريات على ورقة، ثم تترك سطراً فارغاً قبل كتابة ملاحظة للمندوب؛ هذا السطر الفارغ يخبر القارئ فوراً أن القائمة انتهت وبدأ كلام جديد. في رسائل HTTP/1.1، يؤدي السطر الفارغ الفاصل نفس الدور بالضبط: يقع بعد آخر رأس من رؤوس الرسالة وقبل بداية الجسم مباشرةً ليخبر السيرفر أو المتصفح أن «رؤوس الرسالة انتهت هنا، وكل ما يأتي بعد هذا السطر هو محتوى الجسم».
في الكمبيوتر، عندما تضغط مفتاح Enter للانتقال إلى سطر جديد في رسائل HTTP، يضع البروتوكول رمزين غير مرئيين لنهاية السطر يُرمَز لهما بالاختصار CRLF (ويُكتبان برمجياً \r\n). ولأن آخر رأس في الرسالة ينتهي أصلاً بنزول سطر CRLF، فإن ترك سطر فارغ إضافي بعده يعني ضغط Enter مرتين متتاليتين (\r\n\r\n) كما يوضح المثال الآتي:
# 1. Visual representation of CRLF (\r\n) in a bodiless GET request:
GET /courses/http HTTP/1.1\r\n
Host: closetag.com\r\n
Accept-Language: ar, en\r\n
\r\n
# 2. Raw stream view showing Double CRLF (\r\n\r\n) at the end:
# ...Accept-Language: ar, en\r\n\r\n
# ^^^^---- Ends the last header line
# ^^^^ Standalone Blank Line (CRLF)
بمجرد أن يقرأ السيرفر أو المتصفح هذا السطر الفارغ (\r\n\r\n)، يتوقف فوراً عن البحث عن رؤوس جديدة:
- إذا كانت الرسالة بدون جسم (مثل طلب
GETعادي لجلب صفحة): فإن السطر الفارغ يعلن نهاية الرسالة بالكامل ويبدأ السيرفر في الرد فوراً. - إذا كانت الرسالة تحتوي على جسم (مثل إرسال بيانات أو استقبال صفحة
HTML): يقرأ المستقبِل محتوى الجسم بعد السطر الفارغ بعدد البايتات المكتوب في رأس الطولContent-Length. ولهذا السبب، لو كان داخل كودHTMLأوJSONفي الجسم عشرات الأسطر الفارغة، فلن يتلخبط المتصفح أبداً لأنه عبر مرحلة الرؤوس بالفعل كما يظهر في المثال الآتي:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 77
<-- 1st Blank Line: Ends Header Section
<!DOCTYPE html>
<html>
<-- Blank line inside Body (normal text)
<body>
<h1>Welcome to CloseTag</h1>
</body>
</html>
ويشترط معيار HTTP أن يكون السطر الفارغ الفاصل خالياً تماماً من أي مسافات بيضاء أو علامات جدولة قبل فاصل CRLF. فوجود مسافة في بداية السطر لا يُعد سطراً فارغاً في قواعد البروتوكول، بل يُصنف تركيباً غير قياسي مرفوضاً. كما أن إسقاط السطر الفارغ قبل الجسم يجعل المحلل يدمج أول سطر من الجسم مع قسم الرؤوس ويحاول قراءته كرأس. ويؤدي ذلك إلى فشل التحليل أو رفض الرسالة برمز خطأ العميل 400 Bad Request، لذا يُعد فهمه مهارة أساسية في بناء الأنظمة وتشخيص الشبكات.
تفصيل تقني متقدم: الاتصالات المستمرة (Connection Persistence) ومصافحة 100 Continue قبل رفع الأجسام الكبيرة
تظهر الأهمية القصوى للسطر الفارغ CRLF وتأطير الرسائل في خاصيتين معياريتين في HTTP/1.1:
- الاتصالات المستمرة الافتراضية (Connection Persistence —
RFC 9112 §9.3): يُبقيHTTP/1.1اتصالTCPمفتوحاً افتراضياً (Keep-Alive) لإرسال عدة طلبات واستجابات متتالية عبر المقبس نفسه دون إغلاقه؛ وبفضل السطر الفارغCRLFوطول الجسم الدقيق، يعرف المحلل أين تنتهي الرسالة الأولى وأين يبدأ سطر البداية للرسالة الثانية فوراً، ولا يُغلق الاتصال إلا عند إرسال الرأسConnection: close. - الاستجابة المؤقتة
100 Continue(RFC 9110 §10.1.1 & §15.2.1): عندما يعتزم العميل رفع جسم رسالة ضخم ويخشى أن يرفض الخادم الطلب بسبب الصلاحيات أو الحجم، يرسل العميل رأس الطلب فقط متبوعاً بالسطر الفارغCRLFمع رأسExpect: 100-continueوينتظر؛ فإذا وافق الخادم ردّ باستجابة إعلامية مؤقتة خالية من الجسمHTTP/1.1 100 Continue\r\n\r\n، وحينئذٍ فقط يبث العميل بايتات الجسم على الاتصال نفسه.
# A) Persistent TCP Connection (2 requests on the same socket)
GET /app.css HTTP/1.1
Host: www.closetag.com
# Next request on the SAME TCP connection (then close):
GET /app.js HTTP/1.1
Host: www.closetag.com
Connection: close
# B) 100 Continue Flow (Head sent first -> Server approves -> Body sent)
PUT /backups/db-dump.sql HTTP/1.1
Host: storage.closetag.com
Content-Length: 52428800
Expect: 100-continue
# <-- Server replies: HTTP/1.1 100 Continue\r\n\r\n
# --> Client now streams the 50 MB body on the same TCP connection
لفهم الفرق بين رؤوس الرسالة (Headers) وجسم الرسالة (Body) بسهولة، تذكّر دائماً مثال الطرد البريدي: الملصق الخارجي المكتوب عليه عنوان المستلم ووزن الصندوق ونوع البضاعة بالداخل هو رؤوس الرسالة، أما البضاعة نفسها الموجودة داخل الصندوق فهي جسم الرسالة:
- الوظيفة: الرؤوس تحمل معلومات وصفية وتعليمات تحكم تساعد المتصفح والسيرفر على فهم الرسالة والتعامل معها، بينما يحمل الجسم المحتوى الفعلي الذي نريد إرساله أو عرضه (مثل صفحة الموقع، أو صورة، أو بيانات نموذج).
- الشكل وطريقة الكتابة: تُكتب الرؤوس دائماً كأسطر نصية منظمة بصيغة
الاسم: القيمة، أما الجسم فيمكن أن يكون نصاً عادياً أو كودHTMLأو بياناتJSONأو حتى ملف صورة أو فيديو. - المكان والإلزامية: تأتي الرؤوس دائماً قبل السطر الفارغ ولا تخلو منها أي رسالة عملية (لأن رأس
Hostالذي يحدد اسم الموقع إلزامي في كل طلبHTTP/1.1)، بينما يأتي الجسم دائماً بعد السطر الفارغ وهو اختياري قد لا نحتاجه في كثير من الرسائل.
وعندما تحتوي الرسالة على جسم، نضع في الرؤوس معلومتين أساسيتين لمساعدة الطرف المستقبل على قراءة الجسم بشكل صحيح: رأس Content-Type ليخبره ما نوع هذا المحتوى؟ (مثل application/json أو text/html)، ورأس Content-Length ليخبره كم عدد بايتات هذا المحتوى؟ كما في المثال الآتي:
POST /api/progress HTTP/1.1
Host: closetag.com
Content-Type: application/json
Content-Length: 31
{"course": "HTTP", "lesson": 3}
تفصيل تقني متقدم: متى يُمنع وجود جسم الرسالة؟ وتصحيح المفاهيم الشائعة (RFC 9110 & RFC 9112)
يضع المعياران RFC 9112 (القسم 6) و RFC 9110 (الأقسام 8.6 و 9.3 و 15.3) قواعد دقيقة تفصل بين أحكام وجود الجسم في الطلبات مقابل الاستجابات:
- قاعدة وجود الجسم في الطلبات (Requests): نحوياً وفق
RFC 9112، يدل وجود رأسContent-LengthأوTransfer-Encodingفي الطلب على وجود جسم رسالة. أما دلالياً وفقRFC 9110(القسم9.3)، فإن إرسال جسم داخل طلباتGETوHEADوDELETEليس له معنى دلالي معرَّف (No Defined Semantics)، ويوصي المعيار العميل بعدم إرسال جسم معها (SHOULD NOT) لأن الخوادم والوسطاء قد تتجاهله أو ترفض الطلب؛ ولذلك تُرسل هذه الطلبات عملياً دون جسم وتنتهي فور السطر الفارغCRLF. - استجابات
HEADو304 Not Modified(رأس طول بلا جسم فعلي): ينص المعيارRFC 9112على أن أي استجابة لطلبHEADوأي استجابة تحمل الرمز304 Not Modifiedيُحظر قطعياً أن تحتوي على جسم رسالة بعد السطر الفارغ، حتى لو وُجد رأسContent-Lengthداخل الرأس! فوفق المعيارRFC 9110(القسم8.6)، يحق للخادم إرسالContent-Lengthفي استجابةHEADأو304لإخبار العميل بحجم المورد لو طُلب بفعلGET، لكن محلل الشبكة ينهي قراءة الرسالة فوراً عند السطر الفارغCRLFبصفر بايت في الجسم. - ردود 1xx الإعلامية واستجابات
204 No Contentو205 Reset Content: تخلو هذه الاستجابات من جسم الرسالة دائماً؛ فيُحظر معيارياً في ردود1xxو204 No Contentوجود أي جسم أو إرسال رأسContent-LengthأوTransfer-Encoding، كما يحظر المعيارRFC 9110(القسم15.3.6) على الخادم توليد أي جسم في استجابة205 Reset Content(وتُرسل بطول صفريContent-Length: 0).
يوضّح المثال الآتي هذا السلوك المعياري بدقة؛ إذ يرسل العميل طلب HEAD لفحص حجم ملف دون تحميله، فيرد الخادم برأس Content-Length بقيمة 4520 وتنتهي الرسالة فوراً بعد السطر الفارغ دون أي بايت في الجسم:
# === [1] Client sends HEAD to inspect resource metadata only ===
HEAD /manuals/http-rfc-guide.pdf HTTP/1.1
Host: docs.closetag.com
Accept: application/pdf
# === [2] Server responds with GET metadata & 0 body bytes ===
HTTP/1.1 200 OK
Date: Mon, 02 Nov 2026 12:35:00 GMT
Content-Type: application/pdf
Content-Length: 4520
ETag: "pdf-v2-4520"
تفصيل تقني متقدم: النقل المجزأ (Chunked) والرؤوس اللاحقة (Trailers) وحمولات Multipart وقاعدة تعارض التأطير (RFC 9112 §7.1 & RFC 9110 §6.5)
عندما يبث الخادم محتوى ديناميكياً لا يُعرف حجمه الإجمالي مسبقاً، فإنه يستغني عن رأس Content-Length ويرسل بدلاً منها رأس Transfer-Encoding بقيمة chunked؛ حيث يُرسل كل جزء مسبوقاً بحجمه بالنظام الست عشري على سطر ينتهي بـ CRLF، ويُختتم البث بسطر طوله 0.
الرؤوس اللاحقة بعد الجسم (HTTP Trailer Fields — RFC 9110 §6.5 و RFC 9112 §7.1.2): يتيح النقل المجزأ chunked (وكذلك إطار HEADERS الختامي بعد DATA في HTTP/2 و HTTP/3) إرسال حقول رؤوس بعد انتهاء جسم الرسالة تُسمى Trailers؛ وتُستخدم لإرسال بيانات وصفية لا تتوافر قيمتها إلا بعد الانتهاء من توليد الجسم بالكامل، مثل زمن المعالجة الفعلي Server-Timing أو البصمة الرقمية للتحقق من سلامة البيانات Content-Digest، وذلك بعد الإعلان عن اسمه مسبقاً في رأس Trailer داخل الرأس.
القاعدة المعيارية لتعارض Transfer-Encoding و Content-Length (RFC 9112): يحظر المعيار على المرسِل الجمع بين الرأسين في رسالة واحدة. وإذا استُلمت رسالة تجمعهما معاً، فإن Transfer-Encoding تجبُّ Content-Length؛ ونظراً لكون هذا التعارض مؤشراً على هجمات تهريب طلبات HTTP (HTTP Request Smuggling)، يوصي المعيار المستقبِل برفض الرسالة كخطأ (400 Bad Request) أو إغلاق الاتصال، وإذا اختار الخادم الوسيط تمرير الرسالة فيجب عليه إلزاماً (MUST) حذف رأس Content-Length وإزالتها تماماً قبل تمرير الرسالة.
أما عند رفع الملفات مع حقول نصية في طلب واحد، فيُستخدم النوع المتعدد الأجزاء multipart/form-data مع فاصل نصي فريد boundary يقسم جسم الرسالة من الداخل كما في الأمثلة الثلاثة الآتية:
# A) Chunked Response + Trailer Field After Body (RFC 9112 §7.1.2)
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Transfer-Encoding: chunked
Trailer: Server-Timing
8
CloseTag
0
Server-Timing: render;dur=18.4
# B) Multipart Request Body (Form field + uploaded file)
POST /upload HTTP/1.1
Host: www.closetag.com
Content-Type: multipart/form-data; boundary=CTBoundary99
--CTBoundary99
Content-Disposition: form-data; name="course"
HTTP
--CTBoundary99--
يخضع بروتوكول HTTP في إصداره النصي لقواعد نحوية صارمة لا تقبل التسامح في مواضع المسافات والفواصل. فمجرد مسافة في غير موضعها أو شرطة مائلة مفقودة أو سطر فارغ منسي كفيل بجعل الخادم يرفض الرسالة فوراً برمز خطأ العميل 400 Bad Request أو بتعليق الاتصال. وتتركز أبرز الأخطاء الشائعة في صياغة سطر البداية (Start-Line) وحقول الرؤوس في النقاط التالية:
- أخطاء مسار الهدف: تُفصل عناصر سطر الطلب الثلاثة بمسافة قياسية واحدة. لذلك فإن ترك مسافة غير مرمزة داخل مسار الهدف (Request-Target) أو نسيان الشرطة المائلة الأمامية في بدايته يكسر تقسيم سطر البداية ويجعل الخادم يرفض الطلب.
- تشويه صيغة إصدار البروتوكول: كتابة رمز إصدار البروتوكول (HTTP-Version) بمسافة بدلاً من الشرطة المائلة أو بحروف صغيرة يخالف الصيغة القياسية الإلزامية في السطر الأول.
- المسافات البادئة وأسطر التعليقات: لا يدعم بروتوكول
HTTPالنصي الخام إدراج أسطر تعليقات برمجية داخل الرسالة. كما أن وضع مسافة فارغة أو تعليق قبل سطر البداية يفسد تحليله فوراً. - وضع مسافة قبل النقطتين الرأسيتين: يحظر المعيار قطعياً وجود أي مسافة بين اسم الرأس والنقطتين الرأسيتين ويلزم الخادم برفض الطلب برمز
400 Bad Request. أما المسافة الواحدة بعد النقطتين الرأسيتين قبل قيمة الرأس فهي صحيحة ومستحسنة. - ترك مسافة داخل اسم الرأس بإسقاط الواصلة: تُربط الكلمات المركبة في أسماء الرؤوس مثل
Accept-LanguageوContent-Typeبواصلة متصلة دائماً. ولذلك فإن ترك مسافة داخل الاسم يكسر تحليل الرأس فوراً. - إغفال رأس المضيف الإجبارية: يشترط البروتوكول إرفاق رأس
Hostواحد بالضبط في كل طلب قياسي لتحديد النطاق المستهدف. ويؤدي نسيان هذا الرأس أو تكراره في الطلب الواحد إلى رفضه برمز400 Bad Requestكما يوضح المثال المقارن التالي:
# WRONG: Comment line, missing leading slash, unencoded space,
# missing version slash, space in header name, and no Host header:
// bad request example
GET my lessons/http.html HTTP 1.1
Accept Language : ar, en
# CORRECT: Valid start-line, encoded path, and valid headers:
GET /my%20lessons/http.html HTTP/1.1
Host: closetag.com
Accept-Language: ar, en
وإلى جانب أخطاء مقدمة الرسالة، تقع أخطاء شائعة أخرى عند الفصل بين الرؤوس وجسم الرسالة أو عند توصيف طول الجسم ونوعه:
- نسيان السطر الفارغ الفاصل: إرسال الجسم ملتصقاً بآخر رأس دون سطر
CRLFفارغ يجعل المحلل يقرأ أول سطر من الجسم كرأس فيفشل التحليل. كما أن نسيان السطر الفارغ في نهاية طلب خالٍ من الجسم يجعل الخادم يعلق منتظراً بقية الرؤوس. - عدم تطابق طول المحتوى مع حجم الجسم الفعلي: كتابة رقم في رأس
Content-Lengthأصغر من عدد البايتات الفعلي للجسم يؤدي إلى اقتطاع البيانات (Truncation) وبتر نهايتها. أما كتابة رقم أكبر من الحجم الفعلي فتجعل المستقبل ينتظر بايتات إضافية لن تصل أبداً حتى تنقضي مهلة الاتصال. - إغفال رأس نوع المحتوى أو عكس صيغتها: يوصي المعيار
RFC 9110(القسم8.3—SHOULD) بإرفاق رأسContent-Typeبصيغة النوع الرئيسي ثم النوع الفرعي مع كل رسالة تحمل جسماً؛ لأن إغفالها يجعل المستقبل يعامل البيانات كبايتات مجهولة (application/octet-stream) أو يرفض الطلب برمز415 Unsupported Media Type، كما أن عكس صيغتها يفسد تفسير الحمولة كما يبين المثال التالي:
# WRONG: Reversed MIME type (html/text), wrong Content-Length (10
# instead of 31 bytes), and missing Blank Line before the body:
POST /api/progress HTTP/1.1
Host: closetag.com
Content-Type: html/text
Content-Length: 10
{"course": "HTTP", "lesson": 3}
# CORRECT: Valid MIME type, exact 31-byte length, and Blank Line:
POST /api/progress HTTP/1.1
Host: closetag.com
Content-Type: application/json
Content-Length: 31
{"course": "HTTP", "lesson": 3}
وعلى المستوى المفاهيمي والبرمجي، يجب الحذر من افتراض أن كل رسالة HTTP لا بد أن تحتوي على جسم. فاستجابات 1xx و 204 No Content و 205 Reset Content و 304 Not Modified واستجابات HEAD يُحظر معيارياً أن تحمل أي جسم، كما أن طلبات GET و HEAD و DELETE لا تحمل جسماً ذا دلالة معيارية في الاستخدام القياسي. ومن الأخطاء البرمجية الشائعة أيضاً محاولة قراءة رسائل HTTP/1.1 عبر تقسيم النص عشوائياً عند كل سطر جديد؛ فالتحليل الصحيح يلتزم بقواعد تأطير الرسالة (Message Framing) التي تعتمد على فاصل CRLF المزدوج لنهاية الرأس، ثم على Transfer-Encoding (مثل chunked) أو طول البايتات في Content-Length لقراءة الجسم.
تحدي «لاقط الأخطاء» التفاعلي: اكتشف السطر المعطوب في 4 رسائل HTTP خام
اختبر دقة ملاحظتك الهندسية! تحتوي كل رسالة من الرسائل الأربع في الأداة التفاعلية أدناه على خطأ بنيوي أو صياغي واحد يؤدي إلى رفضها برمز 400 Bad Request أو اقتطاع بياناتها (مسافة قبل النقطتين، غياب Host، خطأ Content-Length مع العربية، أو غياب السطر الفارغ CRLF). انقر مباشرةً على السطر الذي يحمل الخطأ لاكتشافه وتصحيحه:
اختبار تطبيقي قصير: قس قدرتك على قراءة رسالة HTTP وتشخيصها بنفسك
قبل الانتقال إلى الاختبار التفاعلي في نهاية الدرس، اقرأ الرسالتين الخام الآتيتين بتمعن وحاول الإجابة عن الأسئلة التشخيصية الخمسة أدناه بنفسك:
# === Message [A]: Raw HTTP Request ===
POST /api/v1/auth/verify HTTP/1.1
Host: auth.closetag.com
Content-Type: application/json
Content-Length: 28
{"user": "ali", "otp": 4821}
# === Message [B]: Raw HTTP Response to a Conditional GET ===
HTTP/1.1 304 Not Modified
Date: Mon, 02 Nov 2026 12:50:00 GMT
ETag: "v9-bundle"
Content-Length: 1420
قارن قراءتك للرسالتين بالإجابات المعيارية الموثقة في الجدول الآتي:
| سؤال قراءة الرسالة وتشخيصها | القراءة الصحيحة والتفسير المعياري |
|---|---|
1. ما صيغة هدف الطلب في الرسالة [A]؟ وكيف يتحول سطر بدايتها في HTTP/2؟ |
الصيغة هي origin-form. وفي HTTP/2 يُستبدل سطر البداية ورأس Host بأربعة حقول زائفة داخل إطار HEADERS هي: method بقيمة POST، و scheme بقيمة https، و authority للنطاق، و path للمسار. |
2. هل قيمة 28 في رأس Content-Length للرسالة [A] مطابقة لطول الجسم الفعلي؟ |
نعم، مطابقة تماماً؛ فكائن JSON المرسل يتكون من أحرف ASCII قياسية يبلغ طولها 28 بايت بالضبط، فيقرأ الخادم الكائن كاملاً دون اقتطاع أو تعليق. |
3. كم بايت سيقرأ المتصفح في جسم الرسالة [B] بعد السطر الفارغ CRLF؟ |
صفر بايت (لا يوجد جسم إطلاقاً)؛ فرغم وجود رأس Content-Length بقيمة 1420، فإن رمز الاستجابة هو 304 Not Modified، وبموجب RFC 9112 تنتهي الرسالة فوراً عند السطر الفارغ، بينما تشير القيمة 1420 فقط إلى حجم الملف المخزن لو طُلب بفعل GET وفق RFC 9110. |
4. ماذا يحدث لو أُضيف رأس Transfer-Encoding بقيمة chunked إلى الرسالة [A] مع بقاء رأس Content-Length؟ |
ينشأ تعارض تأطير خطير يرتبط بثغرات تهريب الطلبات (Request Smuggling)؛ وبموجب RFC 9112 تجبُّ Transfer-Encoding رأس الطول ويوصى برفض الرسالة برمز 400 Bad Request أو إغلاق الاتصال، وإذا اختار الخادم الوسيط تمريرها فيجب عليه إلزاماً (MUST) حذف رأس Content-Length قبل تمريرها. |
ملخص الدرس
حصاد الدرسمراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي
ماذا تعلّمت في هذا الدرس؟
تعلّمت في هذا الدرس أن كل تواصل في HTTP يتم عبر نوعين من الرسائل (الطلب والاستجابة)، وأن الرسالة تتكون من أربعة أقسام: سطر البداية، ورؤوس الرسالة، والسطر الفارغ الفاصل الإلزامي، وجسم الرسالة الاختياري الذي يُقاس طوله بعدد البايتات الفعلي (حيث يشغل كل حرف عربي بايتين في UTF-8)، كما تعرّفت على تحولها إلى إطاري HEADERS وDATA في HTTP/2 وHTTP/3 وفحصتها عملياً.
الخطوة التالية والدرس القادم
طبّق ما تعلمته الآن بحل المهام العملية وبنك الأسئلة التفاعلي أسفل الدرس، ثم انتقل مباشرةً إلى الدرس التالي في المسار: طلب HTTP (HTTP Request) لتتعمق في أفعال الطلب وبناء الطلبات الاحترافية.
سجّل الدخول لحفظ تقدمك
تابع إنجاز المواضيع والمصطلحات والمهام، وكمّل من آخر خطوة على أي جهاز.
المصطلحات8
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 HTTP Message رسالة HTTP
- وحدة البيانات المهيكلة التي يتبادلها العميل والخادم عبر بروتوكول HTTP لإنجاز أي طلب أو استجابة.
- للمراجعة
- 02 Start-Line سطر البداية
- السطر الأول الإلزامي في رسالة HTTP/1.1؛ ويكون سطر طلب في الطلبات أو سطر حالة في الاستجابات.
- للمراجعة
- 03 Request-Target هدف الطلب
- المكوّن الثاني في سطر الطلب لتحديد المورد المستهدف بإحدى الصيغ الأربع في RFC 9112 (الأصل، المطلقة، السلطة، النجمة).
- للمراجعة
- 04 Header Fields حقول الرؤوس
- أسطر بيانات وصفية بصيغة (الاسم: القيمة) غير حساسة لحالة الأحرف تتحكم في التوجيه والتخزين المؤقت والمصادقة وتأطير الجسم.
- للمراجعة
- 05 Blank Line (CRLF) السطر الفارغ الفاصل
- السطر الخالي الإلزامي المكون من محرفَي إرجاع الحامل وتغذية السطر للفصل الحاسم بين الرؤوس وجسم الرسالة.
- للمراجعة
- 06 Message Body جسم الرسالة
- الحمولة الاختيارية التي تلي السطر الفارغ وتحمل البيانات المرسلة أو المورد المسترجع كتيار من البايتات.
- للمراجعة
- 07 Pseudo-Header Fields الحقول الزائفة
- حقول تحكم مسبوقة بنقطتين رأسيتين (:method و:scheme و:authority و:path و:status) تحل محل سطر البداية النصي في HTTP/2 وHTTP/3.
- للمراجعة
- 08 HTTP Request Smuggling تهريب طلبات HTTP
- ثغرة أمنية خطيرة تنشأ عند اختلاف الخادم الوسيط والخادم الخلفي في حساب نهاية الرسالة بسبب تعارض رأسَي Transfer-Encoding وContent-Length.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
الاختصارات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 CRLF إرجاع الحامل وتغذية السطر
- Carriage Return Line Feed
- تسلسل التحكم الثنائي (\r\n أو 0x0D 0x0A) المعتمد معيارياً لإنهاء كل سطر في رسائل HTTP/1.1.
- للمراجعة
- 02 ABNF الصيغة النحوية المعيارية الموسعة
- Augmented Backus-Naur Form
- لغة التوصيف القياسية المعتمدة في وثائق IETF لتعريف القواعد النحوية الدقيقة لرسائل البروتوكول.
- للمراجعة
- 03 HPACK ضغط رؤوس HTTP/2
- Header Compression for HTTP/2
- خوارزمية الضغط الثنائي للرؤوس والحقول الزائفة داخل إطارات HEADERS في بروتوكول HTTP/2.
- للمراجعة
- 04 QPACK ضغط رؤوس HTTP/3
- Header Compression for HTTP/3
- خوارزمية ضغط الرؤوس المصممة لبروتوكول HTTP/3 فوق تدفقات QUIC لمنع حجب طابور الرأس.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
مهامك قبل الاختبار
خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.
-
راجع مثال الطلب المتصل POST /api/v1/orders واستجابته 201 Created، وتحقق من تطابق Content-Length مع حجم كائنَي JSON بالبايت.
-
تحقق من سبب جواز ظهور Content-Length في استجابات HEAD و304 رغم انتهاء الرسالة عند السطر الفارغ بصفر بايت في الجسم.
-
افتح تبويب Network في أدوات المطور (F12) وافحص رؤوس الطلب والاستجابة وتبويبَي Payload وResponse.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي7 أسئلة تنتظرك +
اقرأ رسالة الطلب الخام وحدد صيغة الهدف وطول الجسم
تأمل رسالة طلب HTTP/1.1 الخام أدناه. أي عبارة تصف سطر البداية وجسم الرسالة وصفاً معيارياً دقيقاً؟
POST /api/v1/orders HTTP/1.1
Host: store.closetag.com
Content-Type: application/json
Content-Length: 30
{"item": "Keyboard", "qty": 1} كيف يتعامل المحلل مع استجابة 304 أو HEAD تتضمن Content-Length؟
أرسل المتصفح طلباً شرطياً فاستقبل الاستجابة الخام أدناه. وفق المعيارين RFC 9110 وRFC 9112، كم بايت سيقرأ المتصفح في جسم الرسالة بعد السطر الفارغ؟
HTTP/1.1 304 Not Modified
Date: Mon, 02 Nov 2026 12:50:00 GMT
ETag: "v9-bundle"
Content-Length: 1420
صواب أم خطأ: حساب Content-Length للنصوص العربية بترميز UTF-8
صواب أم خطأ: بما أن كائن JSON النصي {"name": "إبراهيم"} يشغل 18 بايت في ترميز UTF-8 (لأن كل حرف عربي يشغل بايتين)، فإن كتابة Content-Length: 11 ستؤدي إلى اقتطاع الجسم وفشل تحليل JSON لدى الخادم.
كيف تُمثَّل بيانات سطر البداية في HTTP/2 وHTTP/3؟
كيف ينتقل سطر طلب HTTP/1.1 (GET /tutorials HTTP/1.1 مع رأس Host: www.closetag.com عبر HTTPS) عبر الشبكة في بروتوكولَي HTTP/2 وHTTP/3؟
شخّص سبب رفض الخادم لهذا الطلب برمز 400 Bad Request
افحص طلب HTTP/1.1 الخام أدناه. لماذا سيرفضه أي خادم قياسي يلتزم بالمعيار RFC 9112 فوراً برمز 400 Bad Request؟
GET /v1/profile HTTP/1.1
Host: api.closetag.com
Api Key: secret-123
أكمل بناء رسالة طلب HTTP/1.1 الخام
أكمل الفراغين (____) في رسالة طلب HTTP/1.1 الخام أدناه: اكتب مسار الهدف بصيغة الأصل /v1/users في الفراغ الأول، واكتب الطول الدقيق بالبايت (17) لجسم الرسالة {"role": "admin"} في الفراغ الثاني.
POST ____ HTTP/1.1
Host: api.closetag.com
Content-Type: application/json
Content-Length: ____
{"role": "admin"} اختر كل العبارات المعيارية الصحيحة حول رسائل HTTP
اختر جميع العبارات الصحيحة المتوافقة مع معيارَي RFC 9110 وRFC 9112 واترك العبارات الخاطئة.