ما هو HTTP؟ دليل مسؤول الخادم
افهم HTTP من منظور مسؤول الخادم: الطرق ورموز الحالة والرؤوس المهمة، وما يسجله nginx، وكيف يرتبط HTTP/3 وTLS بطلباتك واستجاباتك اليومية.
ما هو HTTP؟
HTTP (بروتوكول نقل النص التشعبي) هو مجموعة القواعد التي يستخدمها العميل وخادم الويب لطلب شيء وإرساله. يرسل العميل طلباً يتضمن طريقة مثل GET، ومساراً مثل /pricing، وإصدار البروتوكول، وقائمة من الرؤوس، وأحياناً نص الطلب. يجيب الخادم برمز حالة مثل 200، ثم يرسل رؤوسه الخاصة وعادةً نص الاستجابة. كل عرض لصفحة وكل استدعاء لـAPI (واجهة برمجة التطبيقات) على خادمك هو عملية التبادل نفسها، وتتكرر باستمرار.
لا يحتفظ HTTP بحالة خاصة به. لا يتذكر الخادم ما طلبته قبل ثانية، لذلك تُنقل أي معلومات تؤدي وظيفة الذاكرة، مثل جلسة تسجيل الدخول، في رأس مع كل طلب. تفسر هذه الخاصية جانباً كبيراً مما يأتي: تعتمد التخزين المؤقت بالكامل على الرؤوس، ويمكن لموازن التحميل إرسال طلبك التالي إلى backend مختلف من دون أن يتسبب ذلك في أي مشكلة.
يوضح كل ما يلي شكل هذا النموذج من جهة الخادم، في access log وفي إعدادات nginx.
طلب واستجابة خامّان مع التعليقات
فيما يلي طلب HTTP/1.1 كامل. ينهي السطر الفارغ الرؤوس، ويكون كل ما بعده هو الجسم. لا يحتوي GET عادةً على جسم.
GET /pricing HTTP/1.1
Host: example.com
User-Agent: curl/8.5.0
Accept: */*
Accept-Encoding: gzipGETهي الطريقة، وتحدد الإجراء المطلوب. تقرأGETالبيانات، وترسلPOSTالبيانات، وتستبدلPUTالمورد، وتزيلDELETEالمورد، وتطلبHEADرؤوسGETمن دون الجسم./pricingهو المسار. لا يكون اسم المضيف جزءاً من سطر الطلب، ولذلك يوجد الرأس التالي.HTTP/1.1هو إصدار البروتوكول الذي يستخدمه العميل.- يحدد
Host: example.comالموقع الذي يريده العميل. يتطلب HTTP/1.1 هذا الرأس، ولذلك يجيب nginx عن طلب يفتقر إليه باستخدام400 Bad Request. - أما البقية فهي تفضيلات. يوضح
Accept-Encoding: gzipأن العميل يستطيع فك الضغط، ولذلك يُسمح للخادم بضغط الجسم.
للاستجابة البنية نفسها، مع وجود سطر الحالة في أعلاها.
HTTP/1.1 200 OK
Date: Thu, 06 Aug 2026 09:12:44 GMT
Server: nginx
Content-Type: text/html; charset=utf-8
Content-Length: 5310
Cache-Control: public, max-age=300
<!doctype html>...200 OKهو رمز الحالة مع عبارة السبب. الرمز هو المهم. أما العبارة فإضافية ويتجاهلها العملاء.- يوضح
Content-Typeللعميل كيفية التعامل مع البايتات التالية. - يحدد
Content-Lengthحجم الجسم بالبايت، لكي يعرف العميل موضع نهايته. عندما لا يكون الحجم معروفاً مسبقاً، يرسل الخادمTransfer-Encoding: chunkedبدلاً منه، ويحدد النهاية بقطعة طولها صفر. - يحدد
Cache-Controlللمتصفح وأي ذاكرة تخزين مؤقت بينهما مدة الاحتفاظ بهذه الاستجابة. - يفصل السطر الفارغ بعد الرؤوس بينها وبين الجسم في الاتجاهين.
أسماء الرؤوس غير حساسة لحالة الأحرف، وينتهي كل سطر بعودة حرفية يتبعها تغذية سطر، وليس بسطر جديد مجرد. لن تكتب هذه القيم يدوياً، لكنك ستصادفها في التقاط الحزم.
لمراقبة زوج فعلي، نفّذ هذا الأمر ضد موقع تملكه:
curl -sS -o /dev/null -D - https://example.com/يكتب -D - رؤوس الاستجابة في الطرفية، بينما يتخلص -o /dev/null من الجسم. استخدمه بدلاً من curl -I، لأن -I يرسل طلب HEAD. سيعرض خادم التطبيقات الذي يتعامل مع HEAD بطريقة مختلفة عن GET، وهذا شائع، رؤوساً لا يتلقاها أي متصفح. يطبع curl -v جانبَي الاتصال، مع وسم سطور الطلب بـ> وسطور الاستجابة بـ<.
شكل سطر الطلب في سجل وصول nginx
يأتي nginx بتنسيق سجل combined، وهذا تعريفه:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';وهذا سطر واحد ينتجه:
203.0.113.45 - - [06/Aug/2026:09:12:44 +0000] "GET /pricing HTTP/1.1" 200 5310 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) Chrome/127.0.0.0 Safari/537.36"203.0.113.45هو$remote_addr، أي العنوان الذي فتح اتصال TCP (بروتوكول التحكم في الإرسال). خلف الوكيل، يكون هذا عنوان الوكيل وليس عنوان الزائر.-الأول هو عنصر نائب ثابت. أما الثاني فهو$remote_user، ولا يُملأ إلا عند استخدام مصادقة HTTP الأساسية."GET /pricing HTTP/1.1"هو$request، أي سطر الطلب المنسوخ تماماً كما وصل.200هي الحالة التي أعادها خادمك، وليست الحالة التي أدركها الزائر.5310هو$body_bytes_sent، أي جسم الاستجابة وحده. لا تُحتسب رؤوس الاستجابة، لذلك يكون هذا الرقم دائماً أصغر من عدد البايتات المُرسلة فعلياً.- الحقلان الأخيران المحاطان بعلامتي اقتباس هما
RefererوUser-Agent. كلاهما يأتي من العميل، لذلك يمكن أن يحتوي أيٌّ منهما على أي قيمة.
بما أنّ $request يُنسخ حرفياً، تظهر البيانات غير الصالحة حرفياً أيضاً. يترك العميل الذي يستخدم TLS (أمان طبقة النقل) على المنفذ 80 غير المشفّر سطر 400 يبدأ فيه حقل الطلب ببايتات مهروبة مثل "\x16\x03\x01\x02\x00\x01". يمثّل \x16 نوع سجل مصافحة TLS، لذلك تكون هذه البايتات بداية ClientHello وليست سطر طلب على الإطلاق. يعمل خادمك بشكل صحيح. هناك جهة ما توجّه HTTPS إلى منفذ HTTP.
أضف $server_protocol إلى تنسيق السجل أيضاً. فهو يطبع HTTP/1.1 أو HTTP/2.0 أو HTTP/3.0، وهذه أسرع طريقة لإثبات أن تغيير البروتوكول دخل حيّز التنفيذ فعلاً.
ما الذي تعنيه رموز الحالة الشائعة عندما يعيدها موقعك
الرقم الأول هو الفئة، وهذه الفئة هي أول ما ينبغي أن تقرأه.
2xx يعني أن الطلب نجح. 200 OK للقراءة العادية. 201 Created بعد POST أنشأ مورداً. 204 No Content لنجاح لا يتضمن أي محتوى لإرساله، وهو الرد المعتاد على DELETE.
3xx يعني الانتقال إلى مكان آخر. 301 دائم ويخزّنه المتصفح بقوة، وأحياناً إلى أن يمسح المستخدم ملفه الشخصي، لذلك يصعب التراجع عن 301 يشير إلى اسم مضيف خاطئ. استخدم 302 أثناء اختبار إعادة التوجيه. 304 Not Modified نجاح وليس خطأ: أرسل العميل If-None-Match يتضمن ETag (وسم الكيان) ما زلت تتعرّف إليه، لذلك أرسلت الرؤوس دون المحتوى. يعني امتلاء السجل بـ304s أن التخزين المؤقت يعمل.
4xx يعني أن الطلب كان خاطئاً. 400 Bad Request يعني أن الإدخال مشوّه. 401 Unauthorized يعني فعلياً أن العميل غير موثَّق، ويجب أن يتضمن ترويسة WWW-Authenticate التي تحدد المخطط. 403 Forbidden يعني أن الطلب فُهم، لكن رُفض رغم ذلك. 404 Not Found هو مسار غير موجود. 405 Method Not Allowed هو المسار الصحيح مع طريقة HTTP خاطئة، وهذا ما يعيده POST إلى موقع ملفات ثابتة. 413 يعني أن المحتوى أكبر من client_max_body_size في nginx، وهو مضبوط افتراضياً على 1 ميغابايت، ويؤكد سجل الأخطاء ذلك باستخدام client intended to send too large body.
وجود 403 عند طلب ملف ثابت يعني في الغالب أن المشكلة في نظام الملفات، لا في قاعدة HTTP. اقرأ /var/log/nginx/error.log قبل تغيير أي إعداد. open() "/srv/site/index.html" failed (13: Permission denied) يعني أن مستخدم worker في nginx لا يستطيع قراءة الملف، وغالباً يكون السبب أن دليلاً أباً يفتقد صلاحية التنفيذ للآخرين. directory index of "/srv/site/" is forbidden يعني أن المسار أُحيل إلى دليل لا يحتوي على ملف فهرس، بينما يكون autoindex معطلاً.
5xx يعني أن المشكلة حدثت في جانبك. 500 هو خطأ غير معالج في تطبيقك. 502 Bad Gateway يعني أن nginx لم يتمكن من الحصول على استجابة صالحة من upstream، ويسمي سجل الأخطاء السبب: connect() failed (111: Connection refused) while connecting to upstream يعني أنه لا توجد خدمة تستمع على العنوان الموجود في proxy_pass. 504 Gateway Timeout يعني أن upstream قبل الاتصال ثم لم يرسل شيئاً خلال proxy_read_timeout، وهي 60 ثانية افتراضياً، ويسجل السجل ذلك على أنه upstream timed out (110: Connection timed out) while reading response header from upstream. 503 Service Unavailable هو رفض متعمد. لاحظ أن محدد معدل الطلبات الخاص بـnginx يعيد 503، لأن limit_req_status مضبوط افتراضياً على 503. إذا كنت تبحث عن 429 Too Many Requests في السجل وتجد 503 بدلاً منه، فهذا هو السبب. اضبط limit_req_status 429; للحصول على رمز الحالة الصحيح.
رؤوس HTTP المهمة عند تشغيل الخادم
Host يحدد الموقع المطلوب. يمكن لعنوان IP واحد تقديم مئات أسماء المضيفين، ويطابق nginx Host مع server_name ليقرر أي كتلة server تستجيب. إذا لم يحدث أي تطابق، يستخدم nginx الخادم الافتراضي، وهو أول كتلة تستمع على ذلك العنوان والمنفذ، ما لم تُحدَّد كتلة أخرى باستخدام default_server. إذا أعاد المضيف الافتراضي الجديد الموقع الخطأ، فالسبب في الغالب هو نفسه: لم يطابق الاسم الطلب، لذلك انتقل الطلب إلى الخادم الافتراضي. اختبر ذلك من دون تعديل DNS:
curl -sS -o /dev/null -D - -H 'Host: app.example.com' http://127.0.0.1/User-Agent وصف يرسله العميل عن نفسه، وهو نص حر. استخدمه كمؤشر عند قراءة السجلات. لا تستخدمه كآلية تحكم، لأن العميل الذي يريد تزويره يستطيع ذلك ببساطة. لذلك فإن حظر أداة كشط استناداً إلى User-Agent سيحظر الأدوات التي تلتزم به فقط.
Content-Type يحدد كيفية تفسير البايتات: application/json لطلب API، وtext/html; charset=utf-8 لصفحة. يربط nginx امتدادات الملفات بالأنواع باستخدام /etc/nginx/mime.types، وتحدد الحزمة nginx.conf القيمة default_type application/octet-stream;. لذلك يُعرض الملف ذو الامتداد الذي لا يعرفه nginx كتنزيل بدلاً من عرضه. وتظهر المشكلة في صفحة تُحمّل من دون تنسيق، بينما تطبع وحدة تحكم المتصفح Refused to apply style from ... because its MIME type ('text/plain') is not a supported stylesheet MIME type. يشير MIME هنا إلى امتدادات بريد الإنترنت متعددة الأغراض، وهو مخطط التسمية الذي تأتي منه سلاسل الأنواع هذه.
Cache-Control هو الوسيلة التي تتحكم بها في كل ذاكرة تخزين مؤقت بين خادمك والقارئ. يناسب public, max-age=31536000, immutable الملفات الثابتة التي يحتوي اسمها على تجزئة للمحتوى، لأن الاسم يتغير عندما يتغير المحتوى. ويجب استخدام no-store لأي محتوى خاص بالمستخدم، لأن ذاكرة التخزين المؤقت المشتركة التي تحتفظ بصفحة بعد تسجيل الدخول ستقدمها إلى الشخص التالي الذي يطلب عنوان URL نفسه. أما private فهو الإعداد المتوسط: يمكن للمتصفح الاحتفاظ بالمحتوى، لكن لا يجوز لذاكرة تخزين مؤقت مشتركة الاحتفاظ به.
X-Forwarded-For موجود لأن الوكيل يخفي عنوان الزائر. بعد مرور الطلب عبر Reverse Proxy، يصبح $remote_addr عنوان الوكيل. لذلك ستتعامل السجلات وتحديد الموقع الجغرافي وتحديد معدل الطلبات مع عميل واحد فقط. يجب على الوكيل تمرير العنوان الأصلي:
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;ويجب بعد ذلك إخبار الخادم المستقبل بالاعتماد على هذا العنوان، وتحديد الجهات التي يُسمح له بالاعتماد عليها بدقة:
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;أدرج النطاقات التي تتحكم بها فقط. إنّ X-Forwarded-For نص عادي يمكن لأي عميل إرساله، ولذلك يتيح set_real_ip_from 0.0.0.0/0; للزائر اختيار العنوان الذي تسجله والعنوان الذي يحتسبه محدد معدل الطلبات.
X-Forwarded-Proto يمنع عطلاً محدداً وشائعاً جداً. ينهي الوكيل TLS ويمرر الطلب إلى التطبيق عبر HTTP عادي. يرى التطبيق طلباً عادياً، ويقرر أن الزائر يجب أن يستخدم HTTPS، ثم يعيد 301 https://example.com/. يتبعه المتصفح، وينهي الوكيل TLS مرة أخرى ويمرر HTTP عادياً مرة أخرى، وتتكرر الحلقة حتى يتوقف المتصفح مع ERR_TOO_MANY_REDIRECTS. يؤدي إرسال X-Forwarded-Proto: https إلى إخبار التطبيق بأن الزائر يستخدم HTTPS بالفعل، لذلك يتوقف عن إعادة التوجيه.
HTTP/1.1 مقابل HTTP/2 مقابل HTTP/3: ما الذي يتغير بالنسبة إليك
HTTP/1.1 بروتوكول نصي، ويتعامل مع طلب واحد في كل مرة لكل اتصال. تتيح Connection: keep-alive للطلب التالي إعادة استخدام اتصال TCP نفسه، ما يوفّر تكلفة إنشاء الاتصال، لكن الاستجابات تظل تصل بالترتيب الذي طُلبت به. تحجب استجابة بطيئة كل ما ينتظر خلفها. يُسمّى ذلك حجب بداية الصف، وتتغلب المتصفحات عليه بفتح عدة اتصالات باسم المضيف نفسه في الوقت ذاته.
يحافظ HTTP/2 على الأساليب نفسها ورموز الحالة نفسها، ويغيّر طريقة تأطير البيانات إلى صيغة ثنائية. تشترك طلبات كثيرة في اتصال واحد بصفتها تدفقات مستقلة، كما يُضغط نص الرؤوس المتكرر، وهذا مهم لأن الطلب الحديث يحمل قدراً كبيراً منه. يظل الاتصال قائماً على TCP، لذلك تؤدي أي حزمة مفقودة إلى إيقاف كل التدفقات على ذلك الاتصال حتى تصل إعادة الإرسال. لم يختفِ حجب بداية الصف، بل انتقل من HTTP إلى طبقة النقل. كان Server Push جزءاً من HTTP/2، لكنه اختفى عملياً لأن Chrome أزال دعمه في 2022.
يحافظ HTTP/3 على الدلالات نفسها مرة أخرى، ويستبدل TCP بـQUIC، وهو بروتوكول نقل مبني على UDP (بروتوكول مخطط بيانات المستخدم). تكون تدفقات QUIC مستقلة حتى في الطبقات الأدنى، لذلك لا توقف الحزمة المفقودة إلا التدفق الذي تنتمي إليه. ويكون TLS 1.3 مدمجاً في مصافحة QUIC بدلاً من وضعه فوقها، لذلك يحتاج الاتصال الجديد إلى عدد أقل من دورات ذهاب وإياب. وينتج عن ذلك أثران عمليان: يجب فتح منفذ UDP 443 في كل جدار ناري على المسار، وأي شبكة تحد من UDP أو تحجبه ستعيد العملاء إلى HTTP/2.
ما الذي يتغير بالنسبة إليك عملياً؟ لا تبدأ المتصفحات أبداً باستخدام HTTP/3. فهي تتصل عبر HTTP/2 أو HTTP/1.1، وترى ترويسة Alt-Svc: h3=":443"; ma=86400 في الاستجابة، ثم تستخدم HTTP/3 للاتصالات اللاحقة مع ذلك المضيف. لذلك فالترويسة ليست إضافة تجميلية اختيارية، بل هي آلية الاكتشاف. في nginx، أصبح HTTP/2 توجيهاً مستقلاً في الإصدار 1.25.1 (http2 on; داخل كتلة server، ليحل محل المعامل القديم listen ... http2)، ووصل QUIC إلى الإصدار الرئيسي 1.25.0، حيث يحتاج موقع HTTP/3 إلى listen 443 quic reuseport; إلى جانب listen 443 ssl; العادي.
تختلف درجة نضج الوكلاء العكسيين في هذا الجانب، ومن المفيد التحقق من الإصدار الذي تشغّله فعلياً. اعتباراً من August 2026، يقدّم Caddy خدمة HTTP/3 افتراضياً دون إعداد. يحتاج nginx إلى مستمع quic صريح، إضافة إلى ترويسة Alt-Svc الموضحة أعلاه. ويفعّل Traefik ذلك لكل entry point من خلال الخيار الصريح http3. إذا أنهيت TLS عند Traefik أمام عدة تطبيقات Docker، فإن إصدار البروتوكول الذي يحصل عليه الزوار يُحدَّد هناك، أما القفزة من الوكيل إلى الحاوية فعادةً فتكون عبر HTTP/1.1 عادي، بغض النظر عن البروتوكول الذي تفاوض عليه المتصفح.
تحقق بدلاً من الافتراض. يعمل curl --http3 -sS -o /dev/null -D - https://example.com/ فقط إذا أدرج curl -V HTTP3 ضمن ميزاته، ومعظم إصدارات التوزيعات لا تتضمنه. والفحص الموثوق هو سجلّك أنت: أضف $server_protocol إلى التنسيق، واقرأ ما تتفاوض عليه المتصفحات الفعلية. وقبل كل ذلك، تأكد من أن UDP 443 مفتوح فعلاً، لأن جداراً نارياً يسمح بـTCP 443 فقط سيؤدي إلى فشل HTTP/3 بصمت، بينما يظل الموقع يعمل عبر HTTP/2. ومعرفة المنافذ المفتوحة والتي تستمع على خادم Linux لديك هي أول ما يجب التحقق منه.
HTTPS: HTTP هو البروتوكول، وTLS هو الغلاف
HTTPS ليس بروتوكولاً منفصلاً. فهو ينقل الطلبات نفسها ورموز الحالة نفسها داخل جلسة TLS. ينقلها المنفذ 80 بنص واضح، بينما ينقلها المنفذ 443 بشكل مشفّر. تكتمل مصافحة TLS أولاً، ثم ينتقل طلب HTTP داخل القناة المشفّرة. ولهذا لا ترتبط مشكلة الشهادة أبداً برمز حالة: يحدث الفشل قبل إرسال أي بايت من HTTP، لذلك لا توجد استجابة تحمل رقماً.
توجد نقطة مهمة تتعلق بالترتيب على خادم يستضيف عدة مواقع. تُختار الشهادة باستخدام SNI (مؤشر اسم الخادم)، وهو حقل في مصافحة TLS يحمل اسم المضيف بنص واضح قبل وجود أي ترويسة HTTP. لذلك يختار الخادم الشهادة من SNI أولاً، ثم يختار المضيف الافتراضي من الترويسة Host ثانياً. هذان بحثان منفصلان، لكنهما يتوافقان عادةً. وعندما لا يتوافقان، يعرض المتصفح خطأ عدم تطابق الاسم مثل NET::ERR_CERT_COMMON_NAME_INVALID ولا يرسل أي طلب على الإطلاق، لأن شهادة الخادم الافتراضي عُرضت لاسم لا تغطيه.
للموقع العام، احصل على شهادة حقيقية ودَعها تجدّد نفسها. يكتب Certbot مع Let's Encrypt على nginx مسارات الشهادة داخل كتلة الخادم، ويثبّت مؤقت التجديد نيابةً عنك. أما اسم المضيف الذي لا تستطيع أي جهة عامة التحقق منه، مثل اسم داخلي أو عنوان IP مجرد على شبكتك الخاصة، فـالشهادة الموقعة ذاتياً على Ubuntu هي الخيار الصريح، ما دمت تقبل بضرورة إخبار كل عميل بالثقة بها.
بعد عمل TLS، أرسل كل شيء على المنفذ 80 إلى المنفذ 443:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}أضف Strict-Transport-Security فقط عندما تكون متأكداً. تخبر الترويسة add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; المتصفحات برفض HTTP غير المشفّر لاسم المضيف ذلك لمدة عامين، وتلتزم المتصفحات بها من ذاكرة التخزين المؤقت الخاصة بها، ما يعني أن إزالة الترويسة لاحقاً لا تلغيها. ابدأ بـmax-age مدته بضع ساعات، وتأكد من أن كل نطاق فرعي يستخدم HTTPS فعلاً، ثم ارفع المدة.
FAQ
ما الفرق بين HTTP وHTTPS؟
HTTPS هو HTTP يُرسل داخل جلسة TLS (أمان طبقة النقل). الطرق ورموز الحالة متطابقة. الذي يتغير هو تشفير البايتات بين العميل وأي مكوّن ينهي TLS، وانتقال المنفذ الافتراضي من 80 إلى 443. بما أنّ مصافحة TLS تكتمل قبل إرسال أول بايت من HTTP، فإن فشل الشهادة لا ينتج رمز حالة أبداً. لذلك يعرض المتصفح تحذير شهادة يتضمن اسماً للخطأ مثل NET::ERR_CERT_COMMON_NAME_INVALID بدلاً من رقم مثل 403.
لماذا يعرض موقعي الخطأ 502 Bad Gateway؟
يعني 502 من nginx أنّ nginx لم يتمكن من الحصول على استجابة صالحة من الخادم الخلفي الذي يوجّه إليه الطلبات. لذلك كان طلب الزائر سليماً، بينما حدث الخلل في مكوّن خلف nginx. اقرأ /var/log/nginx/error.log. يعني connect() failed (111: Connection refused) while connecting to upstream أنّه لا يوجد شيء يستمع على العنوان والمنفذ المحددين في proxy_pass. تحقق من أن التطبيق يعمل وأنه مرتبط بالمكان المتوقع. يعني no live upstreams while connecting to upstream أنّ كل خادم في كتلة الخوادم الخلفية وُسِم بأنه متوقف بعد إخفاقات متكررة. قارنه بـ 504 Gateway Timeout، الذي يعني أنّ الخادم الخلفي قبل الاتصال ثم فشل في الرد خلال proxy_read_timeout.
لماذا يعرض access log عنوان IP نفسه لكل زائر؟
لأنّ $remote_addr يسجل العنوان الذي فتح اتصال TCP. وعند استخدام reverse proxy أو شبكة توصيل محتوى، يكون ذلك العنوان هو عنوان الـproxy. يصل عنوان الزائر في الرأس X-Forwarded-For بدلاً من ذلك. اضبط proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; على الـproxy، ثم اضبط set_real_ip_from في nginx المستقبِل على نطاق عناوين الـproxy، واضبط real_ip_header X-Forwarded-For;. أدرج النطاقات التي تتحكم فيها فقط، لأن ذلك الرأس نص يمكن لأي عميل إرساله. إن وثقت به من الإنترنت بالكامل، فسيتمكن الزائر من اختيار العنوان الذي تسجله والعنوان الذي تطبق عليه حدود المعدل.
هل أحتاج إلى تفعيل HTTP/2 أو HTTP/3؟
يستحق HTTP/2 التفعيل، لأن تفعيله يتطلب توجيهاً واحداً على موقع يستخدم TLS بالفعل، كما يزيل حد الطلبات لكل اتصال الذي يجعل الصفحات التي تحتوي على ملفات صغيرة كثيرة بطيئة. أما HTTP/3 فيوفر فائدة أقل وأقل يقيناً، ويتطلب فتح منفذ UDP 443، إضافة إلى proxy مبني مع دعم QUIC. تذكّر أنّ المتصفحات لا تنتقل إلى HTTP/3 إلا بعد أن ترى رأس Alt-Svc في استجابة سابقة. لذلك لن يتغير شيء من دون ذلك الرأس، مهما كان محتوى سطر listen. أضف $server_protocol إلى تنسيق السجل، وقِس البروتوكول الذي يتفاوض عليه الزوار فعلياً قبل تخصيص وقت لذلك.
ماذا يعني 403 Forbidden عندما يكون الملف موجوداً؟
في الموقع الثابت، يكون 403 عادةً ناتجاً عن أذونات نظام الملفات، لا عن قاعدة HTTP. يعني open() ... failed (13: Permission denied) في /var/log/nginx/error.log أنّ مستخدم worker الخاص بـnginx لا يستطيع قراءة الملف. يحدث ذلك غالباً لأن دليلاً أباً يفتقر إلى إذن التنفيذ للآخرين، لا لأن وضع الملف نفسه خاطئ. يعني directory index of ... is forbidden أنّ الطلب حُلّ إلى دليل لا يحتوي على ملف فهرس، بينما يكون autoindex معطلاً. كما تعيد قاعدة deny صريحة داخل كتلة location المطابقة الخطأ 403. لذلك اقرأ تلك الكتلة عندما لا يذكر سجل الأخطاء شيئاً.