خوادم الويب والوكلاء العكسيون

أساسيات Nginx

22 دقيقة الدرس 2 من 28

أساسيات Nginx

Nginx (تُنطق "إنجين إكس") هو خادم الويب المفضّل في تقريباً كل شركة تقنية كبرى — إذ يعمل بوابةً أمامية لـ Netflix وCloudflare وDropbox وGitHub وأغلب المواقع الأعلى حركةً في العالم. إتقان Nginx من التثبيت حتى نموذج التهيئة الداخلي هو متطلب أساسي لكل دور DevOps إنتاجي. يغطي هذا الدرس النموذج الذهني الكامل: كيف تُشغّل Nginx، وكيف يُنظَّم ملف التهيئة، والمفهومان اللذان ستُهيّئهما مئات المرات في مسيرتك — كتل الـ server وكتل الـ location.

تثبيت Nginx

على أنظمة Ubuntu/Debian الحديثة، المسار الموصى به للتثبيت هو حزمة Nginx الرسمية المستقرة أو الحديثة من مستودع APT الخاص بـ Nginx، وليس الحزمة الافتراضية للتوزيعة. حزمة التوزيعة كثيراً ما تتأخر أشهراً في تصحيحات الأمان والميزات.

# --- Ubuntu 22.04 / 24.04 --- # 1. إضافة مستودع APT الرسمي لـ Nginx sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list # 2. تثبيت الأولوية للمستودع الرسمي على مستودع التوزيعة echo -e "Package: *\nPin: origin nginx.org\nPin-Priority: 900\n" \ | sudo tee /etc/apt/preferences.d/99nginx # 3. التثبيت sudo apt update && sudo apt install -y nginx # 4. التفعيل والتشغيل sudo systemctl enable --now nginx # 5. التحقق nginx -v # nginx/1.26.x (مستقر) أو 1.27.x (حديث) systemctl status nginx curl -I http://localhost # HTTP/1.1 200 OK
ممارسة احترافية: استخدم دائماً المستودع الرسمي لـ Nginx في الإنتاج. الحزمة في Ubuntu/Debian عادةً متأخرة 6-12 شهراً — قد تفتقر إلى تصحيحات أمان حرجة (CVEs) وواجهات برمجية لوحدات أحدث. ثبّت الأولوية كما هو موضح حتى لا تُعيد apt upgrade عن غير قصد التحديث إلى حزمة التوزيعة.

تشريح ملف nginx.conf

يُدار سلوك Nginx بالكامل بلغة تهيئة منظَّمة وحيدة. الملف في /etc/nginx/nginx.conf هو الجذر. فهم نموذج السياق الطبقي هو الخطوة المفاهيمية الأهم.

تُنظَّم التهيئة في سياقات (كتل محددة بـ { })، وتنطبق التوجيهات داخل السياق على ذلك السياق وكل السياقات المتداخلة بداخله. أربعة سياقات ستستخدمها باستمرار:

  • main — السياق العام (بلا أقواس محيطة). يتحكم في عمليات العمال وملف PID وسجل الأخطاء وتحميل الوحدات. ينطبق على العملية بأكملها.
  • events — ضبط نموذج معالجة الاتصال. التوجيه الرئيسي: worker_connections.
  • http — كل ما يخص HTTP/HTTPS. يحتوي كتل الـ server ويضع إعدادات HTTP العامة (أنواع MIME، التسجيل، الضغط، المهلات الزمنية، تجمعات الـ upstream).
  • server — مضيف افتراضي (مكافئ لـ VirtualHost في Apache). يقع داخل http. ستمتلك واحداً لكل نطاق (أو لكل منفذ).
  • location — يطابق نمط مسار URL داخل كتلة server. هذا هو المكان الذي يعيش فيه معظم المنطق الخاص بكل مسار.
Nginx configuration context hierarchy main context (nginx.conf) worker_processes auto; error_log /var/log/nginx/error.log warn; pid /run/nginx.pid; events { } worker_connections 1024; use epoll; http { } access_log ...; gzip on; include mime.types; sendfile on; server { } — example.com listen 443 ssl; server_name example.com; location /api proxy_pass http://app:3000 location / root /var/www; try_files server { } — api.example.com listen 443 ssl; server_name api.example.com; location /v1 proxy_pass http://api:8080 location /health return 200 OK; access_log off;
هيكل سياق تهيئة Nginx: main يُحيط بـ events وhttp؛ http يحتوي كتل server (المضيفات الافتراضية)؛ كل server يحتوي كتل location تطابق مسارات URL.

ملف nginx.conf الابتدائي الأنيق الذي تستخدمه الشركات الكبرى في الإنتاج هو أكثر نحافةً بكثير من الملف الافتراضي الذي يشحن به Nginx. ابدأ بهذا الهيكل:

# /etc/nginx/nginx.conf — خط إنتاجي أساسي user nginx; worker_processes auto; # عامل واحد لكل نواة CPU error_log /var/log/nginx/error.log warn; pid /run/nginx.pid; events { worker_connections 1024; # الحد الأقصى للاتصالات المتزامنة لكل عامل use epoll; # نموذج أحداث النواة (الأسرع على Linux) multi_accept on; # قبول أكبر عدد ممكن من الاتصالات لكل استيقاظ } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; # استخدام sendfile() للنواة — نسخ صفري للملفات الثابتة tcp_nopush on; # تجميع حزم TCP (يعمل مع sendfile) tcp_nodelay on; # تعطيل Nagle لاتصالات keep-alive keepalive_timeout 65; gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml; include /etc/nginx/conf.d/*.conf; # تهيئات كل موقع تقع هنا }
الفكرة الأساسية: worker_processes auto يجعل Nginx يُنشئ تحديداً عامل واحد لكل نواة CPU منطقية. كل عامل أحادي الخيط ويعالج آلاف الاتصالات بشكل غير متزامن باستخدام حلقة أحداث epoll (Linux) أو kqueue (macOS/BSD). هذا مختلف جوهرياً عن نموذج Apache الذي يخصص خيطاً لكل اتصال، وهو سبب استخدام Nginx جزءاً صغيراً من الذاكرة تحت التزامن العالي.

كتل الـ Server — الاستضافة الافتراضية

كتلة الـ server هي وحدة الاستضافة الافتراضية في Nginx. كل كتلة تُجيب على الطلبات لمجموعة محددة من عنوان IP والمنفذ ورأس Host (أي server_name). عند وصول طلب، يختار Nginx كتلة الـ server الصحيحة وفق خوارزمية مطابقة محددة:

  1. مطابقة عنوان listen والمنفذ تحديداً.
  2. من بين الكتل المطابقة، إيجاد واحدة يطابق server_name فيها رأس Host تحديداً.
  3. إن لم توجد مطابقة تحديدية، التحقق من وجود wildcard بادئ (*.example.com)، ثم wildcard لاحق.
  4. إن لم تُوجد مطابقة بعد، فحص أسماء server المعتمدة على regex بترتيب التعريف.
  5. الرجوع إلى default_server على ذلك المنفذ.

في الإنتاج ستُعلّم دائماً صراحةً إحدى كتل الـ server بـ default_server على المنفذ 80 و443، وتلك الكتلة يجب أن تُعيد 444 (Nginx يُغلق الاتصال دون استجابة) لأسماء المضيفين غير المعروفة — هذا يمنع الماسحين من تحديد بصمة الخادم الخلفي بضربه عبر الـ IP.

# /etc/nginx/conf.d/default.conf — catch-all، يرفض الطلبات المباشرة عبر IP server { listen 80 default_server; listen [::]:80 default_server; server_name _; # يطابق أي شيء غير مُطالَب به كتلة أخرى return 444; # إغلاق صامت — لا استجابة HTTP تُرسَل } # /etc/nginx/conf.d/example.com.conf — المضيف الافتراضي الحقيقي server { listen 80; listen [::]:80; server_name example.com www.example.com; # إعادة توجيه كل HTTP إلى HTTPS (301 واحدة، بلا خطر حلقة إعادة توجيه) return 301 https://example.com$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; # تفعيل HTTP/2 (Nginx 1.25.1+) server_name example.com www.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; root /var/www/example.com/public; index index.html index.php; # منطق كتل الـ location يأتي هنا include /etc/nginx/snippets/security-headers.conf; }
مصيدة إنتاجية: لا تُغفل أبداً كتلة catch-all بـ default_server. بدونها، أول كتلة server مُعرَّفة على منفذ تصبح الافتراضية الضمنية. هذا يعني أن ماسحاً يضرب خادمك عبر IP سيحصل على استجابة من مضيفك الافتراضي الأول — مما قد يُسرّب اسم نطاقه أو شهادة TLS أو تقنية الخادم الخلفي. عرّف دائماً catch-all صريحاً بـ return 444.

كتل الـ Location — توجيه URL

داخل كتلة server، تُوجّه كتل location الطلبات إلى معالجات مختلفة بناءً على مسار URL. يستخدم Nginx خوارزمية أولوية محددة لاختيار كتلة الـ location الفائزة — الخطأ في هذا الفهم هو أحد أكثر أخطاء التهيئة الإنتاجية شيوعاً.

معدّلات كتلة location (بترتيب الأولوية):

  • = /exact — مطابقة سلسلة تحديدية؛ أعلى أولوية، تُوقف البحث فوراً.
  • ^~ /prefix — مطابقة بادئة تفوز على أي regex؛ إن تطابقت، تُوقف البحث عن regex.
  • ~ pattern — regex حساس لحالة الأحرف (أول تطابق يفوز بين كتل regex).
  • ~* pattern — regex غير حساس لحالة الأحرف.
  • /prefix — أطول مطابقة بادئة (بلا معدّل)؛ أقل أولوية، تُستخدم فقط إن لم يتطابق أي regex.
# /etc/nginx/conf.d/example.com.conf — أمثلة على كتل location server { listen 443 ssl; server_name example.com; root /var/www/example.com/public; # 1. مطابقة تحديدية — الأسرع، تُوقف كل مطابقة إضافية location = /favicon.ico { access_log off; log_not_found off; expires 30d; } # 2. مطابقة بادئة تفوق regex — الأصول الثابتة تتجاوز PHP location ^~ /assets/ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # 3. regex غير حساس لحالة الأحرف — ملفات PHP location ~* \.php$ { fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } # 4. أطول بادئة احتياطية — catch-all لـ SPA أو front-controller في PHP location / { try_files $uri $uri/ /index.php?$query_string; } # 5. وكيل API — مطابقة بادئة، تفوّض لخادم التطبيق upstream location /api/ { proxy_pass http://127.0.0.1:3000/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
الفكرة الأساسية — try_files: التوجيه try_files $uri $uri/ /index.php?$query_string هو أساس كل نشر لـ Laravel أو Symfony أو تطبيق أحادي الصفحة. يتحقق Nginx أولاً إن كان URI يُخطط إلى ملف حقيقي، ثم مجلد حقيقي، ولا يقع في front-controller PHP إلا حين لا يوجد أيٌّ منهما. هذا يعني أن الملفات الثابتة تُخدَّم بكود C الخاص بـ Nginx مباشرةً — PHP لا تستيقظ أبداً لطلب .js أو .css.

اختبار التهيئة وإعادة التحميل

اختبر التهيئة دائماً قبل تطبيقها. خطأ نحوي في nginx.conf حي يجعل nginx -s reload يرفض التغيير — لكن خطأً يُدخَل بينما العملية متوقفة سيمنع الإطلاق كلياً.

# اختبار نحو التهيئة (تشغيل جاف، لا يُطبَّق) sudo nginx -t # متوقع: nginx: configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful # اختبار وطباعة التهيئة المُدمجة كاملاً (مفيد لتصحيح ملفات include) sudo nginx -T | grep -A5 "server_name" # إعادة التحميل بدون إسقاط الاتصالات (يُرسل SIGHUP لعملية master) sudo nginx -s reload # أو عبر systemd (مفضّل — يندمج مع إشراف الخدمة) sudo systemctl reload nginx # إعادة التشغيل الكاملة (تُسقط الاتصالات النشطة — استخدم فقط عند الضرورة) sudo systemctl restart nginx # طباعة الإعدادات الافتراضية المُدمجة وقائمة الوحدات nginx -V 2>&1 | tr -- '-' '\n' | head -20
ممارسة احترافية: في pipelines CI/CD التي تنشر تغييرات تهيئة Nginx، شغّل دائماً nginx -t كفحص مسبق قبل أن يُنفّذ الـ pipeline أي شيء مدمّر. فشل اختبار التهيئة يجب أن يُوقف النشر. في شركات كـ Cloudflare، تمر تغييرات تهيئة Nginx بنفس pipeline مراجعة الكود والتحقق الآلي كالكود التطبيقي — تهيئة سيئة تصل إلى الإنتاج هي حادثة P0.

كيف يختار Nginx كتلة Server وLocation: تتبع طلب

تتبع طلب واحد عبر شجرة قرار Nginx يبني النموذج الذهني الذي تحتاجه لتصحيح مشاكل الإنتاج بسرعة. لطلب https://example.com/api/users?page=2:

  1. يصل اتصال TCP على المنفذ 443. ينظر Nginx في كل كتل server بـ listen 443.
  2. يتم مصافحة TLS؛ SNI تُوفر اسم المضيف example.com.
  3. يمسح Nginx توجيهات server_name: المطابقة التحديدية تفوز على wildcard.
  4. داخل كتلة server المختارة، يُقيّم Nginx كل كتل location. المسار هو /api/users. لا يوجد تطابق تحديدي (= /api/users)، ولا ^~ بادئة، ولا regex مطابق. أطول مطابقة بادئة هي /api/.
  5. كتلة /api/ تُوكّل إلى خادم التطبيق upstream.
  6. يضع Nginx رؤوس الوكيل (X-Forwarded-For، X-Real-IP، Host) ويُعيد توجيه الطلب عبر اتصال TCP داخلي أو Unix socket.
  7. تعود استجابة upstream؛ يبثّها Nginx للعميل ويسجّل الطلب.
نمط فشل شائع — الشرطة المائلة اللاحقة في proxy_pass: proxy_pass http://backend:3000/ (بشرطة مائلة لاحقة) يحذف بادئة location من URI قبل الإعادة. proxy_pass http://backend:3000 (بدون) يحتفظ بها. طلب /api/users مع location /api/ يذهب إلى http://backend:3000/users (بشرطة) أو http://backend:3000/api/users (بدون شرطة). هذا التعارض يُسبب أخطاء 404 يصعب تحديدها بدون قراءة سجلات وصول الـ upstream.

متغيرات رئيسية وتوجيهات مضمّنة

يكشف Nginx عشرات المتغيرات في سياق location. أكثرها استخداماً:

  • $uri — URI الطلب الحالي المُعيَّر (بلا سلسلة استعلام).
  • $args / $query_string — جزء سلسلة الاستعلام.
  • $request_uri — URI الأصلي الكامل مع سلسلة الاستعلام (غير مُعيَّر).
  • $remote_addr — IP العميل (لكن هذا IP موازن الحمل خلف وكيل؛ استخدم $http_x_forwarded_for بعد التحقق من الثقة).
  • $host — قيمة رأس Host، أو اسم الخادم إن كان غائباً.
  • $schemehttp أو https.
  • $document_root — قيمة توجيه root لكتلة location الحالية.

بنهاية هذا الدرس يجب أن تستطيع تثبيت Nginx من المستودع الرسمي، وقراءة وكتابة nginx.conf منظَّم جيداً، وإنشاء مضيفات افتراضية بكتل server مناسبة، وتوجيه الطلبات عبر كتل location باستخدام المعدّل الصحيح لكل حالة استخدام. الدرس التالي يبني على هذا لتغطية خدمة الملفات الثابتة وتكامل PHP-FPM وخلفيات التطبيقات بالتفصيل.