تصل استجابة HTTP إلى المتصفح كاملة في ثلاثة أجزاء متتابعة: سطر الحالة (status line)، ثم سطور الترويسات (headers)، ثم الجسم (body). غير أن المتصفح يكتفي بعرض الجسم وحده صفحة منسقة على الشاشة، ويخفي ما يسبقه من بيانات وصفية. ولفحص طلبات HTTP واستجاباتها كما تتبادلها البرامج فعلا، تبرز أداتان واسعتا الاستخدام: أداة curl التي ترسل طلبا من سطر الأوامر وتطبع ما عاد إليها، ولوحة الشبكة (Network panel) في أدوات المطور التي تسجل كل طلب ترسله الصفحة المفتوحة.
ولا تقتصر فائدة هذه القراءة على مجرد رؤية شكل الرسالة؛ فكثير مما يقال عن معنى طريقة أو رمز حالة أو ترويسة يمكن اختباره عمليا بطلب أو طلبين: يرسل الطلب، ثم يقرأ ما عاد في الاستجابة، فتظهر القاعدة متحققة أو يتبين استثناؤها. والتدرب على الأداتين قبل الخوض في تلك المعاني يجعل كل مفهوم لاحق في الدورة قابلا للتحقق والتجربة المباشرة من أول لقاء به.

curl: عميل HTTP في سطر الأوامر
سطر الأوامر (command line) نافذة نصية يكتب فيها المستخدم أمرا فينفذه النظام، وتسمى بحسب نظام التشغيل الطرفية (terminal) أو موجه الأوامر (Command Prompt) أو PowerShell. وأداة curl برنامج مفتوح المصدر يعمل من داخل هذه النافذة، ويؤدي دور العميل في تبادل HTTP: يستقبل رابطا، ويرسل إلى خادمه طلبا واحدا، ثم يطبع ما وصله إلى الشاشة وينتهي عمله.
ولا يحتاج curl غالبا إلى تثبيت؛ فهو مدمج افتراضيا في نظامي Windows 10 وWindows 11 وفي macOS، ويتوفر لمختلف توزيعات Linux بأمر تثبيت واحد عبر مدير الحزم إن لم يكن موجودا. ويكفي لتجربته تنفيذ هذا الأمر، الذي يطلب النطاق example.com، وهو نطاق محجوز للأمثلة في الوثائق القياسية:
curl https://example.com/<!doctype html><html lang="en"><head><title>Example Domain</title>...</html>يطبع curl هنا جسم الاستجابة وحده: مستند HTML الذي كان المتصفح سيتولى تصييره صفحة مرئية. أما سطر الحالة والترويسات فقد وصلا إلى curl بالفعل مع الجسم، لكنه لا يطبعهما على الشاشة ما لم يطلب منه المستخدم ذلك صراحة. فهو في وضعه الافتراضي يشبه المتصفح في النتيجة النهائية: يسلم المحتوى ويتجاهل ما يصفه.
وعند تشغيل الأداة في نظام Windows، يطرأ أمران عمليان يغيران ما يظهر على الشاشة:
الأول أن الكلمة curl في بيئة Windows PowerShell (وهي النسخة الكلاسيكية المدمجة في النظام) ليست برنامج curl الحقيقي، بل اسم مستعار (alias) لأمر داخلي يسمى Invoke-WebRequest، وهذا الأمر لا يقبل خيارات curl القياسية. ولتفادي ذلك، يكتب اسم الملف التنفيذي كاملا curl.exe بدلا من curl.
والأمر الثاني أن نسخة curl التي تدرجها مايكروسوفت (Microsoft) في النظام بنيت دون دعم لمكتبة HTTP/2، ولذا يظهر في مخرجاتها البروتوكول HTTP/1.1 في المواضع التي يظهر فيها HTTP/2 في أمثلة هذا الدرس.
وعندما يحتوي الرابط المطلوب على معاملات استعلام متعددة تفصل بينها علامة &، يجب إحاطة الرابط بالكامل بين علامتي تنصيص مزدوجتين:
curl "https://example.com/search?q=http&page=2"السبب في ذلك أن العلامة & تمثل رمزا وظيفيا خاصا لدى مفسر سطر الأوامر نفسه قبل أن يصل الرابط إلى curl؛ إذ تفيد تشغيل الأوامر في الخلفية أو الفصل بينها. ومن دون علامتي التنصيص يقطع سطر الأوامر الرابط عند علامة & أو يعترض على صياغة الأمر كله، فلا يصل المعامل page=2 إلى الخادم إطلاقا.
الخيار -i: سطر الحالة والترويسات قبل الجسم
تتيح الخيارات (options) تعديل سلوك curl الافتراضي وتحديد ما يرسله وما يعرضه، وتكتب بعد اسم الأمر مباشرة: إما في صورة حرف مفرد تسبقه شرطة واحدة مثل -i، وإما في صورة كلمة كاملة تسبقها شرطتان مثل --http1.1. وأول هذه الخيارات فائدة في قراءة HTTP هو الخيار -i (اختصار --include)، الذي يجعل curl يطبع سطر الحالة وترويسات الاستجابة قبل الجسم:
curl -i https://example.com/HTTP/2 200
date: Mon, 14 Sep 2026 13:14:27 GMT
content-type: text/html
server: cloudflare
last-modified: Fri, 11 Sep 2026 17:42:00 GMT
allow: GET, HEAD
accept-ranges: bytes
age: 2715
cf-cache-status: HIT
cf-ray: a3afa7a29bfce00a-AMS
<!doctype html><html lang="en"><head><title>Example Domain</title>...</html>تظهر الاستجابة هنا بأجزائها التي يرتبها HTTP: سطر أول يحمل رمز الحالة (status code) 200، ثم ترويسة في كل سطر باسمها وقيمتها، ثم سطر فارغ، ثم الجسم. وقيم ترويسات مثل date وage وcf-ray تتغير من طلب إلى آخر، فلن تطابق الأرقام الظاهرة عند تجربة الأمر، أما البنية الهيكلية للرسالة فتبقى واحدة.
وفي هذا المخرج تفصيلان يلفتان من يعرف شكل سطر الحالة في HTTP/1.1: غياب عبارة السبب (reason phrase) بعد الرمز، وظهور أسماء الترويسات كلها بأحرف صغيرة. وسببهما معا أن الطرفين (curl والخادم) اتفقا على استخدام HTTP/2. فهذه النسخة لا تنقل سطر حالة نصيا، بل تنقل الرمز في حقل ترويسة زائف (pseudo-header field) اسمه :status، وتشترط كتابة أسماء الترويسات بحروف صغيرة داخل إطاراتها الثنائية. فالسطر HTTP/2 200 صاغه curl ليعرض الرمز في صورة مألوفة للمطور. ويمكن طلب HTTP/1.1 صراحة بالخيار --http1.1:
curl -i --http1.1 https://example.com/HTTP/1.1 200 OK
Date: Mon, 14 Sep 2026 13:14:43 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
last-modified: Fri, 11 Sep 2026 17:42:00 GMT
allow: GET, HEAD
...عادت عبارة السبب OK في سطر الحالة، وجاءت أسماء الترويسات بالحالة التي كتبها بها الخادم دون توحيد، فاختلطت فيها الحروف الكبيرة بالصغيرة، كما في Date وlast-modified. وظهرت أيضا ترويستان لم تكونا في استجابة HTTP/2، هما Transfer-Encoding وConnection؛ لأنهما تخصان حدود الجسم وإدارة الاتصال في HTTP/1.1 وحده، ولكل منهما موضعه المفصل في الدورة.
ويقابل الخيار -i خيار آخر قريب منه في الكتابة لا يختلف عنه إلا في حالة الحرف، وهو الخيار بالحرف الكبير -I (المعادل لـ --head):
curl -I https://example.com/يطبع هذا الأمر سطر الحالة والترويسات بلا جسم، فيبدو للوهلة الأولى مجرد اختصار لـ -i يحذف الجسم الطويل. غير أن الفارق الحقيقي في الطلب نفسه لا في العرض: فالخيار -I يرسل الطلب بطريقة أخرى اسمها HEAD بدلا من GET، يطلب بها العميل ترويسات الاستجابة دون محتوى جسمها. ومواصفة البروتوكول (RFC 9110) تطلب من الخادم أن يرد على HEAD بالترويسات التي كان سيرسلها مع GET، لكنها تجيز له أن يحذف منها ما لا تعرف قيمته إلا أثناء توليد الجسم الفعلي. لذلك قد لا تطابق ترويسات -I ترويسات الاستجابة التي يتلقاها طلب GET بدقة.
الخيار -v: الطلب والاستجابة معا
يعرض الخيار -i جانب الاستجابة وحده، ويبقى ما أرسله curl إلى الخادم غير ظاهر. أما الخيار -v (واسمه الوضع المفصل، verbose)، فيطبع سطر الطلب وترويساته، ثم سطر الحالة وترويسات الاستجابة، ومعهما تقرير curl الكامل عن خطوات الاتصال. وهذا مقتطف مما يطبعه للرابط نفسه، استبدلت السطور الكثيرة فيه بعلامة ...:
curl -v https://example.com/* Host example.com:443 was resolved.
* IPv4: 104.20.23.154, 172.66.147.243
* Trying 104.20.23.154:443...
* Connected to example.com (104.20.23.154) port 443
...
* ALPN: server accepted h2
...
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://example.com/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: example.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: example.com
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/2 200
< date: Mon, 14 Sep 2026 13:27:07 GMT
< content-type: text/html
< server: cloudflare
...
<
<!doctype html><html lang="en"><head><title>Example Domain</title>...</html>
* Connection #0 to host example.com left intactتبدأ أغلب هذه السطور بعلامة رمزية تبين مصدر كل سطر:
*تقرير من curl نفسه: لا يرسل على الشبكة ولا يستقبل منها، بل يصف الخطوات الداخلية التي نفذها البرنامج. ففي أول السطور ترجم curl اسم المضيفexample.comإلى عناوين IP، ثم فتح اتصالا بأحدها عند المنفذ 443 (وهو المنفذ الافتراضي لمخططhttps). وتليها سطور تصف إعداد القناة المشفرة التي يقوم عليها HTTPS. ثم يعلن السطرALPN: server accepted h2أن الطرفين اتفقا عبر تفاوض بروتوكول طبقة التطبيقات (Application-Layer Protocol Negotiation, ALPN) على استخدام HTTP/2، واسمها الرمزي المختصر h2.>ما أرسله curl: سطر الطلب وترويساته الصادرة إلى الخادم.<ما استقبله curl: سطر الحالة وترويسات الاستجابة الواردة من الخادم.- سطور بلا علامة: جسم الاستجابة، تماما كما يطبعه curl في وضعه الافتراضي.
ومع أن الاتصال مشفر، تظهر هذه السطور نصا مقروءا بوضوح، لأن برنامج curl يمثل طرفا أصيلا في الاتصال نفسه: يقرأ الطلب قبل أن يشفره، ويقرأ الاستجابة بعد أن يفك تشفيرها. أما من يراقب حركة الشبكة بين الطرفين فلا يرى المحتوى النصي لهذه الرسائل. ولوحة الشبكة في المتصفح تقرأ رسائل الصفحة للسبب ذاته، لأن المتصفح طرف اتصال مباشر كذلك.
والطلب الذي أرسله curl هنا قصير ومحدد. أول ما يعرضه منه السطر GET / HTTP/2، وفيه الطريقة (method) GET وهدف الطلب (request target) /، وهو مسار الرابط https://example.com/. ثم تليه ثلاث ترويسات أساسية فقط: Host باسم المضيف، وUser-Agent التي يعرف فيها العميل بنوع برنامجه وإصداره curl/8.5.0، وAccept بالقيمة */*. ثم يأتي السطر > الفارغ، وهو الفاصل الذي يعلن نهاية الترويسات. هذه الترويسات الثلاث هي كل ما يضيفه curl إلى طلب GET من تلقاء نفسه، في حين يضيف المتصفح عددا أكبر بكثير منها كما سيتضح في لوحة الشبكة.
وفي هذا الطلب ما لم يمر على أسلاك الشبكة بهذه الصيغة الحرفية. فالاتصال قائم بنسخة HTTP/2، ولا وجود لسطر طلب نصي في هذه النسخة. وما أرسله curl فعلا هو الحقول الظاهرة بين قوسين مربعين قبل سطور > في المقتطف: الحقول الزائفة مثل :method و:path و:authority، ومعها الحقول العادية مثل user-agent وaccept، مجمعة داخل إطار ثنائي. أما السطر > GET / HTTP/2 وما تحته فيعرضه curl بصيغة تشبه HTTP/1.1 لأنها أسهل في القراءة البشرية، ولهذا تظهر فيه ترويسة Host نصا مع أن HTTP/2 ينقل اسم المضيف في الحقل الزائف :authority.
طلب تصنعه بنفسك: الخيارات -H و-d و-X
يقتصر الطلب الذي يرسله curl افتراضيا على طريقة GET وثلاث ترويسات أساسية. غير أن اختبار كثير من قواعد HTTP يتطلب بناء طلب مخصص: استخدام طريقة أخرى، أو تمرير ترويسة معينة، أو إرسال جسم يحمل بيانات. ولكل متطلب من هذه المتطلبات خيار مخصص في curl.
وحين ترسل بيانات في جسم الطلب، لا يطبع الخيار -v محتوى هذا الجسم في مخرجاته، بل يكتفي بسطر الطلب وترويساته. وللتأكد من وصول الطلب بكامل تفاصيله كما استقبله الخادم، تفيد الاستعانة بخدمة الصدى (echo service): وهي خادم ويب لا يقدم محتوى خاصا به، بل يعيد في جسم استجابته تحليلا شاملا للطلب الذي استقبله. ومن أشهر هذه الخدمات العامة خدمة httpbin.org، التي تعيد عند طلب المسار /anything الطريقة والترويسات والبيانات كما وصلت إليها:
curl -H "User-Agent: lesson-client" -d "comment=clear" https://httpbin.org/anything{
"form": {
"comment": "clear"
},
"headers": {
"Accept": "*/*",
"Content-Length": "13",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "lesson-client",
"X-Amzn-Trace-Id": "Root=1-6aa7f3d1-3458ac587c5ca6ea3da7fed3"
},
"method": "POST",
"url": "https://httpbin.org/anything"
}يوضح هذا المقتطف من استجابة الخادم أثر كل خيار استعملناه:
-Hيضيف ترويسة مخصصة: يكتب بعده سطر الترويسة كاملا بين علامتي تنصيص بصيغةالاسم: القيمة. وإذا كان الاسم لترويسة يرسلها curl تلقائيا، حلت القيمة المحددة محل القيمة الافتراضية؛ ولهذا استقبل الخادم الترويسةUser-Agentبالقيمةlesson-clientبدلا من القيمة الافتراضيةcurl/8.5.0.-dيضع بيانات في جسم الطلب: وصل النصcomment=clearوتعرف عليه الخادم في حقل الاستمارةform. ويحدث هذا الخيار تعديلين تلقائيين في بنية الطلب دون أن يطلبهما المستخدم صراحة: يبدل طريقة الطلب الافتراضية لتصبحPOSTبدلا منGET، ويضيف ترويستين جديدتين هماContent-Lengthبالقيمة 13 (وهي عدد بايتات النص المرسلcomment=clear)، وترويسةContent-Typeبالقيمةapplication/x-www-form-urlencodedلبيان نوع تنسيق الجسم.
أما الترويسة X-Amzn-Trace-Id الظاهرة في الرد، فلم يرسلها curl إطلاقا، بل أضافها وسيط شبكي (موازن حمل) يمر الطلب عبره قبل بلوغ تطبيق httpbin. وهذا التمايز يبرز الفارق الجوهري بين الخيار -v وخدمة الصدى: الخيار -v يوضح ما أرسله العميل من جهته، بينما تكشف خدمة الصدى ما تسلمه تطبيق الخادم في النهاية بعد عبور الوسطاء.
والخيار الثالث هو الخيار -X، ووظيفته تغيير كلمة الطريقة المكتوبة في سطر الطلب:
curl -X PUT -d "comment=clear" https://httpbin.org/anythingفتعيد خدمة الصدى القيمة "method": "PUT". والملاحظة الدقيقة هنا أن الخيار -X لا يفعل شيئا سوى استبدال كلمة الطريقة نصيا؛ فهو لا يغير المنطق الداخلي لبرنامج curl ولا ما يتوقعه من سلوك الخادم. ولهذا السبب لا يصلح استخدام -X HEAD بديلا عن الخيار -I: ففي اتصال HTTP/1.1 يظل curl مع -X HEAD عالقا ينتظر استقبال جسم لن يرسله الخادم أبدا، في حين يدرك مع -I أن الاستجابة مقتصرة على الترويسات فقط فيتعامل معها بنجاح. ودلالات طريقة PUT وغيرها من الطرق يفرد لها الدرس التالي موضعا مستقلا.
لوحة الشبكة: سجل كل طلب ترسله الصفحة
يوفر المتصفح نفسه أدوات المطور (Developer Tools, DevTools): وهي مجموعة لوحات متكاملة مدمجة في متصفحات Chrome وEdge وFirefox لمعاينة الصفحة المفتوحة وتشخيصها. ومن بين هذه اللوحات لوحة الشبكة (Network panel)، التي تختص بتسجيل كل طلب ترسله الصفحة وكل استجابة ترجع إليها.
تفتح أدوات المطور في متصفحي Chrome وEdge بالضغط على المفتاح F12 أو بالاختصار Ctrl+Shift+I (وفي macOS بالاختصار Cmd+Option+I)، ثم يختار منها تبويب Network. وفي متصفح Firefox يفتح الاختصار Ctrl+Shift+E (أو Cmd+Option+E في macOS) لوحة الشبكة بصورة مباشرة. وتعتمد الشروح الآتية تسميات متصفح Chrome، وهي مطابقة لمتصفح Edge وقريبة جدا من تسميات Firefox.
والقاعدة التشغيلية الأولى في لوحة الشبكة أنها لا تسجل الطلبات إلا أثناء فتحها؛ فالطلبات التي أرسلتها الصفحة قبل فتح أدوات المطور لا تظهر في سجلها قط. ولذلك تكون الخطوة البديهية الأولى دائما بعد فتح اللوحة إعادة تحميل الصفحة لتسجيل نشاطها من البداية. فإذا فتح الرابط https://example.com/ ثم أعيد تحميل الصفحة واللوحة قيد التشغيل، يظهر في جدول السجل صف يحمل الاسم example.com، وتظهر في عمود Type القيمة document (للدلالة على مستند HTML الرئيسي)، بينما يوضح عمود Status رمز حالة الاستجابة.
ويمثل كل صف يظهر في السجل تبادلا مستقلا: طلبا واحدا والاستجابة العائدة عنه. والصفحات الحديثة التي تشتمل على صور وملفات تنسيق ونصوص برمجية تملأ السجل بعشرات الصفوف المتتابعة. ولتنظيم هذا السيل، توفر اللوحة أزرار تصفية أعلى الجدول: مثل الزر Doc لعرض مستند الصفحة فقط، والزر Fetch/XHR لحصر الطلبات البرمجية التي ترسلها نصوص الصفحة، وأزرار أخرى لكل نوع من أنواع الموارد.
وقد يظهر في عمود الحالة الرمز 304 بدلا من 200 عند إعادة التحميل، وسببه أن المتصفح يستفسر من الخادم عما إذا كانت النسخة المخزنة لديه من زيارة سابقة لا تزال صالحة. ولتجنب هذا السلوك أثناء التجارب، يفعل الخيار Disable cache المتاح أعلى اللوحة؛ ومعه يطلب المتصفح المورد كاملا من الشبكة في كل مرة ويظهر رمز النجاح 200.
كما يتضمن السجل أعمدة مفيدة تكون مخفية افتراضيا، يمكن إظهارها بالنقر بالزر الأيمن للفأرة على شريط عناوين الجدول واختيار اسم العمود المطلوب. ومن أنفعها عمود البروتوكول (Protocol)، الذي يكشف إصدار HTTP المستخدم في نقل كل طلب: مثل http/1.1، أو h2 لـ HTTP/2، أو h3 لـ HTTP/3. وقد يختلف الإصدار المسجل هنا عن الإصدار الذي استعمله curl مع الخادم ذاته؛ لأن اختيار الإصدار يخضع لنتيجة التفاوض المشترك بين العميل والخادم بحسب ما يدعمه كل طرف.
وإلى جانب Disable cache، يتصدر اللوحة خيار تحكم رئيسي آخر هو Preserve log: فالسجل يمسح تلقائيا بمجرد انتقال التبويب إلى صفحة جديدة، وتفعيل Preserve log يمنع هذا المسح ويبقي صفوف الصفحة السابقة مسجلة للمقارنة. وتتضح أهمية خيار Disable cache أيضا عند مراقبة عمود الحجم (Size): فقد تظهر فيه عبارة (memory cache) أو (disk cache) بدلا من حجم البيانات بالبايت، ومعناها أن المتصفح استرجع الصفحة من ذاكرته المحلية دون إرسال طلب فعلي عبر الشبكة. وتفعيل هذا الخيار يعطل تلك الآلية ما دامت أدوات المطور مفتوحة، فيجبر المتصفح على جلب الموارد من الخادم مباشرة.
تبويب Headers: قراءة طلب واحد
يؤدي النقر على أي صف في سجل الشبكة إلى فتح لوحة جانبية تعرض تفاصيل ذلك التبادل عبر عدة تبويبات متخصصة. وأول هذه التبويبات هو تبويب الترويسات (Headers)، وينقسم داخليا إلى ثلاثة أقسام منظمة:
- قسم عام (General): يلخص البيانات المحورية للتبادل: الرابط المطلوب (Request URL)، وطريقة الطلب (Request Method)، ورمز الحالة (Status Code)، وعنوان IP للجهة البعيدة ورقم المنفذ المتصل به (Remote Address).
- ترويسات الاستجابة (Response Headers): قائمة الترويسات التي أعادها الخادم مع رده.
- ترويسات الطلب (Request Headers): قائمة الترويسات التي صاغها المتصفح وأرسلها إلى الخادم.
وعند مقارنة ترويسات الطلب التي يعرضها المتصفح لرابط المثال بما عرضه curl، يظهر أمران دقيقان:
الأول أنه في حال نقل الطلب عبر HTTP/2 أو HTTP/3، يعرض متصفح Chrome في صدارة القائمة حقولا تبدأ بنقطتين رأسيتين: مثل :authority و:method و:path و:scheme. وهي الحقول الزائفة ذاتها التي أرسلها curl في مخرجات -v، غير أن Chrome يعرضها بأسمائها الصريحة ضمن قائمة الترويسات، بينما يدمجها curl في سطر محاكى > GET / HTTP/2 وترويسة Host.
والأمر الثاني أن المتصفح يرسل حشدا كبيرا من الترويسات يتجاوز بكثير ترويسات curl الثلاث الافتراضية؛ فيرسل ترويسات لتحديد لغات العرض المفضلة مثل accept-language، وترويسات لأمان السياق مثل sec-fetch-mode، وترويسة user-agent بقيمة طويلة ومفصلة تبين نوع المتصفح ومحركه ونظام التشغيل. فالرابط المستهدف واحد، لكن الطلبين يختلفان اختلافا جذريا في محتواهما الوصفي، وهو ما قد يدفع الخادم إلى إرجاع استجابتين مختلفتين تماما للرابط نفسه.
ويعرض المتصفح أسماء الترويسات افتراضيا مرتبة أبجديا بتنسيق مريح للعين. وبجوار عنوان كل قسم يظهر زر تحويل يسمى Raw (أو view source في بعض الإصدارات)، والنقر عليه يعرض نص الترويسات بترتيب ورودها أو إرسالها الفعلي، وهو ما يماثل البنية النصية الأصلية للرسالة. ويوفر متصفح Firefox خيارا مطابقا يحمل اسم Raw أيضا.
وفي بعض الحالات، يظهر أعلى قائمة ترويسات الطلب تنبيه نصه Provisional headers are shown. ومفاده أن المتصفح لا يعرض ترويسات أرسلت بالفعل عبر السلك؛ وتنشأ هذه الحالة حين لا يمر الطلب على الشبكة أصلا: كأن ترد الاستجابة مباشرة من الذاكرة المؤقتة، أو تعترض الطلب إضافة برمجية في المتصفح فتحجبه، أو يكون الطلب معلقا في انتظار الإرسال. فالترويسات المعروضة هنا مجرد تقدير لما كان المتصفح يعتزم إرساله، وليست نص طلب شبكي مكتمل.
وتكتمل لوحة الفحص بعدة تبويبات مجاورة تفصل جوانب الرسالة الأخرى:
- الحمولة (Payload): يعرض البيانات التي حملها الطلب إلى الخادم، سواء أكانت معاملات استعلام في الرابط أم بيانات نصية في جسم الطلب. ولا يظهر هذا التبويب إلا إذا كان الطلب يتضمن استعلاما أو جسما بالفعل، ويقابله في متصفح Firefox تبويب يسمى Request.
- الاستجابة (Response): يعرض محتوى جسم الاستجابة كنص خام مجرد.
- المعاينة (Preview): يعرض جسم الاستجابة بصورة منسقة ومفسرة: فالصورة تظهر كصورة مرئية، وبيانات JSON تعرض في هيئة شجرة قابلة للطي والفتح.
- التوقيت (Timing): يوضح تفكيكا زمنيا دقيقا للمراحل التي استغرقها التبادل، بدءا من انتظار الاتصال وترجمة الاسم حتى اكتمال وصول آخر بايت.
Copy as cURL: من المتصفح إلى الطرفية
يفسر الفارق بين طلب المتصفح وطلب curl إشكالية شائعة تصادف المطورين: رابط يعمل بصورة صحيحة في المتصفح لكنه يعيد خطأ أو نتيجة مغايرة عند طلبه عبر curl؛ والسبب أن الخادم يتلقى طلبين مختلفين اختلافا تاما من حيث الترويسات وبيانات الهوية. ولإعادة إنتاج طلب المتصفح ذاته من سطر الأوامر بدقة، تتيح لوحة الشبكة نسخ الطلب في هيئة أمر curl مكتمل وجاهز للتنفيذ: وذلك بالنقر بالزر الأيمن للفأرة على صف الطلب المطلوب، ثم اختيار Copy، ثم النقر على Copy as cURL.
وفي بيئة نظام Windows، يوفر متصفح Chrome صيغتين مختلفتين للنسخ: الأولى Copy as cURL (cmd) الموجهة لنافذة موجه الأوامر الكلاسيكية، والثانية Copy as cURL (bash) الموجهة لبيئات سطر أوامر bash؛ والعلة في ذلك أن القواعد التي تحكم علامات التنصيص والرموز تختلف بين البيئتين: فموجه الأوامر يستعمل علامة الإقحام ^ لمتابعة السطر وعلامات التنصيص المزدوجة، بينما تستعمل صدفة bash الشرطة المائلة العكسية \ مع علامات التنصيص المفردة. أما في متصفح Firefox فيتوفر الخيار تحت مسمى Copy as cURL مباشرة في القائمة السياقية للطلب.
ويكون الأمر المنسوخ طويلا ومفصلا؛ لأنه ينقل الترويسات التي شكلها المتصفح كافة، مفردا لكل ترويسة منها خيار -H مستقل. وهذه صورة مختصرة تبين هيكله العام:
curl 'https://example.com/' \
-H 'accept: text/html,application/xhtml+xml,...' \
-H 'accept-language: ar,en;q=0.9' \
-H 'sec-fetch-mode: navigate' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...'وتتجلى القيمة العملية الكبرى لهذا الأمر عند لصقه في الطرفية؛ فإضافة الخيار -i أو -v إليه تتيح لك معاينة الاستجابة الحقيقية التي يتلقاها المتصفح. وعند الشك في أثر ترويسة معينة على سلوك الخادم، يكفي حذف سطر خيار -H الخاص بها وإعادة تنفيذ الأمر لمراقبة النتيجة؛ فهذا الأسلوب القائم على تغيير عنصر واحد في كل مرة ومقارنة الاستجابتين هو الوسيلة المباشرة لعزل المتغيرات وفهم تأثير أي ترويسة عمليا. كما يتيح متصفح Firefox أسلوبا داخليا بديلا من خلال خيار التعديل وإعادة الإرسال (Edit and Resend)، الذي يسمح بتعديل الطريقة والرابط والترويسات والجسم مباشرة وإعادة الإرسال من داخل أدوات المطور دون مغادرة المتصفح.
استجابة برمز خطأ أم لا استجابة أصلا
عندما يطلب curl موردا غير موجود على خادم يعمل بصورة طبيعية، يتسلم العميل استجابة HTTP مكتملة الأركان:
curl -i https://example.com/missingHTTP/2 404
date: Mon, 14 Sep 2026 13:27:07 GMT
content-type: text/html
server: cloudflare
...تتضمن هذه الاستجابة سطر حالة وترويسات وجسما يوضح الخطأ، كأي استجابة نظامية أخرى. فالخادم استقبل الطلب بنجاح، وعالجه، ثم أصدر رسالة HTTP تامة يحمل سطر حالتها الرمز 404. ولا يعد برنامج curl هذه الحالة فشلا في عمله؛ لأنه أدى مهمته بنجاح بإرسال الطلب واستقبال الرد، أما دلالة الرقم 404 فترجع إلى منطق التطبيق لا إلى بروتوكول النقل. وإذا أردت إلزام curl بمعاملة رموز الأخطاء (من 400 فما فوق) كفشل تنفيذي يقطع العمليات المؤتمتة في السكربتات، يمكنك إضافة الخيار -f (أو --fail)؛ ومعه يمتنع curl عن طباعة محتوى الجسم ويكتفي بإخراج رسالة خطأ قياسية مثل curl: (22) The requested URL returned error: 404.
أما الحالة الثانية، فتمثل فشلا من أصل مختلف جذريا:
curl -i https://no-such-host.invalid/curl: (6) Could not resolve host: no-such-host.invalidلا وجود هنا لسطر حالة ولا لترويسات. فالنطاق .invalid نطاق محجوز في معايير الإنترنت (RFC 2606) للأسماء غير الصالحة، ولذلك يعجز نظام أسماء النطاقات عن ترجمته إلى عنوان IP. وعندما لم يعثر curl على عنوان رقمي، لم يستطع فتح اتصال TCP أصلا، ولم يرسل أي طلب HTTP عبر الشبكة. والرقم 6 هنا هو رمز خطأ تشغيلي خاص ببرنامج curl يحدد سبب تعثره محليا، وليس رمز حالة من رموز HTTP؛ إذ لم يتصل بأي خادم ولم يتلق أي إجابة.
وينعكس هذا الفارق البنيوي ذاته داخل لوحة الشبكة في المتصفح: فالطلب الذي يرجع باستجابة 404 يظهر في عمود Status بالرمز 404، وتتوفر له ترويسات استجابة يمكن فحصها في تبويب Headers. أما الطلب الذي يتعثر قبل بلوغ الخادم أو قبل تلقي الرد، فيظهر في عمود Status بالكلمة (failed) وتوضح أدوات المطور سببه التقني (مثل net::ERR_NAME_NOT_RESOLVED عند فشل ترجمة اسم النطاق)، ولا تتوفر له أي ترويسات استجابة.
والقاعدة التشخيصية الثابتة هنا هي: وجود رمز الحالة، أيا كانت قيمته العددية، يثبت أن خادما قد استقبل الطلب وأجاب عنه برسالة HTTP صحيحة. أما غياب رمز الحالة بالكامل فيعني أن دورة HTTP لم تكتمل أصلا: إما لتعثر المبادلة في مرحلة استباقية سابقة للبروتوكول (أثناء ترجمة الاسم عبر DNS، أو مصافحة فتح اتصال TCP، أو تأسيس القناة المشفرة بـ TLS)، وإما لانقطاع الاتصال المفاجئ قبل وصول بايتات الرد.
ما تعرضه الأداة وما يمر على الشبكة
تعرض أدوات الفحص رسائل HTTP بصيغ بصرية مختلفة مصممة لتيسير الفهم البشري؛ فأداة curl تضيف في الوضع المفصل العلامتين > و< لتمييز الاتجاه، وتصيغ لرسائل HTTP/2 سطر حالة نصيا افتراضيا لم يعبر أسلاك الشبكة بهذا القالب. ولوحة الشبكة في المتصفح ترتب الترويسات هجائيا ليسهل البحث فيها، وتدرج الحقول الزائفة المسبوقة بنقطتين ضمن قائمة الترويسات العادية. ولا شيء من هذه المظاهر التنسيقية يمثل بنية الرسالة كما تنتقل بين الأجهزة.
والذي يظل ثابتا لا يتغير بين الأدوات، ولا بين مختلف إصدارات بروتوكول HTTP، هو المعنى الدلالي المجرد: الطريقة المطلوبة، وهدف الطلب، ورمز الحالة، واسم كل ترويسة وقيمتها. فعند فحص أي قاعدة أو التحقق من سلوك بروتوكولي، يجب تركيز المقارنة على هذه العناصر المعنوية الأصيلة دون الانشغال بطريقة كل أداة في صياغتها وعرضها. وبفضل هذا التمييز، يملك المطور في هاتين الأداتين وسيلة عملية لاختبار ما تنص عليه معايير الويب والتأكد من تطبيقها في واقعه البرمجي.
اختبر فهمك
ثمانية أسئلة قصيرة تغطي أفكار الدرس. اختر إجابة ثم اضغط «تحقق» لترى إن كانت صحيحة ولماذا. وفي آخر الاختبار تظهر نتيجتك، ومع كل سؤال أخطأت فيه زر يعيدك إلى القسم الذي يشرحه.
curl -i مع الرابط، لأنه يعرض الاستجابة دون أن يغير الطلبcurl -I مع الرابط، لأنه يعرض الترويسات نفسها بلا جسمcurl مع الرابط وحده، لأنه يطبع الترويسات قبل الجسمcurl -X HEAD مع الرابط، لأنه يطلب الترويسات وحدها-i يطبع سطر الحالة والترويسات والجسم دون أن يمس الطلب. أما -I فيرسل الطلب بطريقة HEAD، والمواصفة تجيز للخادم أن يحذف من استجابة HEAD ترويسات لا تعرف قيمتها إلا أثناء توليد الجسم، فلا تطابق بالضرورة استجابة GET.curl -v ظهرت هذه السطور متتابعة: * Connected to example.com (104.20.23.154) port 443 ثم > Accept: */* ثم < content-type: text/html. أيها يصف شيئا أرسله curl إلى الخادم؟> تسبق ما أرسله> تسبق ما أرسله curl، والعلامة < تسبق ما استقبله، والعلامة * تسبق تقرير curl عن خطوة قام بها ولم ترسل على الشبكة. فسطر Accept من الطلب، وسطر content-type من الاستجابة.curl -v في اتصال بـHTTP/2 السطر > GET / HTTP/2. هل مرت هذه البايتات على الشبكة بهذا النص؟> منسوخ حرفيا من الطلب> من أول السطر فقط:method و:path داخل إطار ثنائي. ويعرض curl الطلب بصيغة قريبة من HTTP/1.1 ليسهل قراءته، والعلامة > نفسها من إضافته.curl -d "name=sara" https://httpbin.org/anything دون الخيار -X. بأي طريقة وصل الطلب إلى خدمة الصدى؟GET، لأن الطريقة لا تتغير ما لم يستعمل -XPUT، لأن -d يرسل بيانات تحدث الموردPOST، لأن -d يجعل curl يرسل البيانات بهذه الطريقةHEAD، لأن الطلب لا ينتظر جسما في الاستجابة-d يغير الطريقة إلى POST من تلقاء نفسه، ويضيف curl معه ترويسة Content-Length بالقيمة 9، وهي طول name=sara. أما -X فلا يلزم إلا لتبديل كلمة الطريقة إلى غيرها.curl -i صفحة بالإنجليزية. ما أنسب خطوة لمعرفة سبب الاختلاف؟-I، لأن طلب HEAD يعرض الترويسات كلها--http1.1، لأن نسخة HTTP تحدد لغة الصفحةcurl -i https://api.example.test/users السطر curl: (6) Could not resolve host: api.example.test وحده. ما رمز الحالة الذي أرسله الخادم؟/users غير موجود
