docs:best-nginx-conf

إعدادات خادم الويب NginX الفضلى

خادم NginX (يلفظ engine X أي المحرك س) هو خادم ويب بديل عن apache وهو يقدم الكثير من المزايا القوية وهو أكثر قوة ومتانة لأنه event based

الحزم الضرورية

تحتاج تثبيت حزمة nginx ولدعم php تحتاج حزمة php-fpm المتوفرة فيدورا 15 وبالتالي أعجوبة 5

yum install nginx php-fpm
service php-fpm start
service nginx start

يمكنك ضبط بدء التشغيل التلقائي للخدمتين php-fpm و nginx إن شئت

chkconfig --levels=2345 php-fpm on
chkconfig --levels=2345 nginx on

وفى أعجوبة/فيدورا +15 ( باستخدام systemd )

systemctl enable php-fpm 
systemctl enable nginx

في الإصدارات الأقدم تحتاج بناء تلك الحزمة من المصدر. يمكنك استعمال ملف src.rpm الخاص بفيدورا 15

wget http://download.fedoraproject.org/pub/fedora/linux/releases/15/Everything/source/SRPMS/php-5.3.6-2.fc15.x86_64.src.rpm
rpmbuild --rebuild php-5.3.6-2.fc15.x86_64.src.rpm
cd ~/rpmbuild/RPMS/x86_64
ls

أبسط الإعدادات

الإعدادات العامة تكون في ملف nginx.conf داخل /etc/nginx/ وإعدادات كل عائل افتراضي Virtual Hosting في /etc/nginx/conf.d/ مثل default.conf وهو في فيدورا 15 يحتوي الإعدادات للنطاق _ أي إن لم يطابق أي Virtual Hosting في الإصدارات السابقة كان ذلك مدموج مع nginx.conf

vim /etc/nginx/nginx.conf
vim /etc/nginx/conf.d/default.conf

التعليمات الموجودة في ملفات الإعدادات تكون ضمن أقسام رئيسية منها

  • قسم http وهي إعدادات عامة مشتركة وهو يحتوي واحد أو أكثر من أقسام server
    • قسم server وهي خاصة بالاستضافة الافتراضية حيث نحدد المنفذ والاسم وغيرها وهو قد يحتوي على أكثر من قسم location
      • قسم مطابقة العناوين location حيث نطبق الإعدادات فقط على الطلبات التي توافق هذا الطلب

إن كنت تريد تجربة الخادم على منفذ مختلف عن منفذ 80 (الذي يأخذه أباتشي) ريثما تقرر الانتقال إلى هذا الخادم عليك تعديل سطر listen في قسم server

server {
    listen       8080;
# ...

لعمل استضافة افتراضية انسخ ملف default.conf إلى ojuba_org.conf مثلا وقم بالتعديلات اللازمة. أول شيء ستحتاج تعديله هو اسم الخادم وكل النطاقات التي تشير له

server {
    listen       8080;
    server_name  ojuba.org *.ojuba.org;
# ...

يمكن وضع تعليمات تحديد جذر الملفات root وتحديد ملفات الفهرس index داخل قسم location (كما في الإعدادات التي تأتي مسبقا) لكن هذا خطأ وعليك نقلها إلى قسم server هذا مهم جدا لعمل php

server {
    listen       8080;
    server_name     ojuba.org *.ojuba.org;
    root   /var/www/ojuba_org/public_html/;
    index  index.php index.html index.htm;
# ...

أما قسم العناوين فلن نحتاج أن نضع فيه أكثر من مجرد مطابقة مع / وهي أول جزء من كل العناوين ونطلب التحقق من وجود الملف try_files أولا كما هو ثم تجربته مع / إضافية ثم تجربة /index.php فإن لم يجد أي من كل ذلك فليعتبره غير موجود وذلك بإعادة 404

server {
    listen        8080;
    server_name   ojuba.org *.ojuba.org;
    charset utf-8;

    root          /var/www/ojuba_org/public_html/;
    index         index.php index.html index.htm;

    location / {
               try_files       $uri $uri/ /index.php =404 ;
    }
# ...

<note tip>ربما تفضل أن تضع سطر try_files خارج location وداخل server </note>

<note important>انتبه للمسافة قبل =404</note>

القاعدة السابقة تشبه إعدادات أباتشي التالية (المستخدمة لعمل clean urls من داخل php في العديد من الأطر أو أنظمة إدارة المحتوى CMS وغيرها)

RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php

إزالة www أو إضافتها

الطريقة الصحيحة لإزالة www. حتى لا يكون هناك محتوى مزدوج في محركات البحث كما في وثيقة http://wiki.nginx.org/Pitfalls

server {
  server_name www.example.com;
  return 301 $scheme://domain.com$request_uri;
}
server {
  server_name example.com;
  [...]
}

الطريقة السابقة توفر عملية مقارنة REGEX بعكس الطريقة الشائعة

server {
  server_name domain.com *.domain.com;
  if ($host ~* ^www\.(.+)) {
    set $raw_domain $1;
    rewrite ^/(.*)$ $raw_domain/$1 permanent;
last;
  }
}

تشغيل php

أغلب الوثائق والإعدادات التلقائية تأتي بإعدادات خاطئة بل ومخترقة وقد ذكرنا سابقا الخطأ الشائع بوضع root و index داخل location مما يجعل الخادم بلا قيمة افتراضية لل root والصواب أن يوضع داخل قسم server وإن شئت غيرته داخل أي location تحتاج أن تغيره فيه.

أما الآن فسنتكلم عن ثغرة مهم في الإعدادات الشائعة وهي أنها تنفذ php بناءا على انتهاء العنوان url ب .php مما يمكن المخترق من رفع ملف يحمل الاسم bug.gif لكنه يحتوي على كود php يريد تنفيذه على الخادم ثم يقوم بعد رفعه بفتح الرابط

http://example.com/images/bug.gif/anything.php

وهذا يجعل الخادم ينفذ الملف على أنه php.

الطريقة الصحيحة لإعداد php هي

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ \.php$ {
          # check existence
          if (!-f $document_root$fastcgi_script_name) { return 404; }
          include        fastcgi_params;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
          fastcgi_pass   127.0.0.1:9000;
        }

لاحظ أننا طلبنا منه التحقق من وجود الملف.

<note important> استخدام if خطير جدا ولا يسمع عمل أي شيء فيه إلا return أو rewrite XYZ last انظر http://wiki.nginx.org/IfIsEvil </note>

ومن الأفضل أيضا التأكد من مكان وجود ملفات php فإن كانت داخل مجلد يسمح للناس الرفع فيه عدم تنفيذ الكود

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ \.php$ {
          # check existence
          if (!-f $document_root$fastcgi_script_name) { return 404; }
          # secure upload directory
          if ($uri ~ "^/images/") { return 404; }
          include        fastcgi_params;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
          fastcgi_pass   127.0.0.1:9000;
        }

تحسين الأداء

يمكنك أن تحسن الأداء بإضافة الترويسة Cache-Control والتي يمكن ضبطها بحيث تخبر المتصفحات بأن لا تطلب الملف مجددا خلال فترة ما فلنقل 24 ساعة وهذا يستخدم غالبا مع ملفات js و css والصور

# public caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires 24h;
}

تفعيل الضغط

في قسم http يمكنك وضع

  gzip             on;
  gzip_vary        on;
  gzip_types       text/javascript text/css text/xml application/xml application/xml+rss;
  gzip_comp_level  9;
  gzip_min_length  100;
  gzip_buffers 16 8k;
  gzip_proxied     expired no-cache no-store private auth;
  gzip_http_version 1.0;
  gzip_disable     "MSIE [1-6]\.";

<note tip> يمكنك توفير الكثير من وقت المعالجة إن وضعت gzip_static مكان كل gzip فيما سبق وهذا سيؤدي إلى تكون ملف .gz مجاور للملف الأصلي عوضا عن ضغطه في كل مرة </note>

التحكم في الوصول

في هذا القسم سنذكر عدد من الإعدادات الشائعة التي قد ترغب في إضافتها لمنع الوصول لبعد الملفات.

قد يكون لديك إعدادات apache بين ملفاتك تحتوي معلومات سرية مثل ملفات .htaccess و .htpasswd

location ~ /\.ht.* {
        deny all;
        access_log off;
        log_not_found off;
}

علامة ~ بعد عبارة location تعني مطابقة الأنماط القياسية REGEX

إن كان الكود يأتي من git و svn فإنك لا تريد أن يصل أحدهم إلى مجلدات تلك الأنظمة ونعمل ذلك بطريقة مشابهة للذي قبله هكذا:

location ~ /\.(?:git|svn).* {
        deny all;
        access_log off;
        log_not_found off;
}

غالبا ترغب في إخفاء إصدار الخادم عبر server_tokens off كذلك لابد من حصر الطرق في GET و HEAD و POST ويكون ذلك بوضع التالي في قسم server

# note: 444 response means that the server returns no information to the client and closes the connection (useful as a deterrent for malware).
server_tokens off;
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
         return 444;
}

<note tip> كود الاستجابة رقم 444 في الحقيقة ليس كود استجابة حقيقي http response code بل هو كود خاص بخادم nginx ويعني إغلاق الاتصال فورا دون رد. ويستخدم في مواجهة البرامج الضارة ومحاولات الاختراق. </note>

حتى تمنع المواقع الأخرى من تحميل الصور عندك وعرضها عندهم يمكنك عمل

location /images/ {
   valid_referers none blocked example.com *.example.com;
   if ($invalid_referer) {
     return   403;
   }
}

يمكنك أيضا عمل redirect لصورة أخرى (شعار الموقع أو صورة توحي بالمنع كما في الضفضدع المتجمد في موقع ImageShack) أو إلى الصفحة التي تعرض الصورة عوضا عن الصورة (كما تفعل code.google.com في صفحات ال downloads) وذلك بوضع التالي

  rewrite ^/images/.*\.(gif|jpg|jpeg|png)$ http://www.examples.com/banned.jpg last;

للمزيد انظر

زيادة الأمن

في قسم http يمكنك تحديد الاتصالات المتزامنة حتى 10 اتصالات لكل مصدر IP أما رقم 5m هنا هو حجم الهاش.

limit_zone my_limits $binary_remote_addr 5m;
limit_conn my_limits 10;

يمكنك تحديد الأحجام والأزمان القصوى

## Start: Size Limits & Buffer Overflows ##
  client_body_buffer_size  1K;
  client_header_buffer_size 1k;
  client_max_body_size 1k;
  large_client_header_buffers 2 1k;
## END: Size Limits & Buffer Overflows ##
## Start: Timeouts ##
  client_body_timeout   10;
  client_header_timeout 10;
  send_timeout          10;
## End: Timeouts ##

<note important> تحديد client_max_body_size السابق يجعل من المستحيل عمل upload لذا عليك استثناء صفحات الرفع وذلك بزيادة client_max_body_size 20m و client_body_timeout 60 </note>

توزيع الحمل على عدة خوادم

يمكن عمل ذلك من خلال upstream

upstream _example_com_loadbalancer  {
ip_hash;
   server 10.0.0.2 weight=2;
   server 10.0.0.3 max_fails=3 fail_timeout=10s;
}

server {
   listen 8080;
   server_name .example.com;
   location / {
      proxy_set_header Host $host;
      proxy_pass  http://_example_com_loadbalancer;
   }
}

في المثال السابق قمنا بتمرير الطلبات إلى خادمين هما 10.0.0.2 و 10.0.0.3 موزعة ثلثاهم للأول وثلث للثاني بالتساوي يمكننا تحديد العديد من الخيارات منها متى نعتبر أن أحدهما معطل ونحول لغيره وتحديد وزن كل خادم (وبالتالي النسبة التي تحول له) من خلال weight …إلخ كذلك يمكن استخدام الخوارزمية ip_hash حتى تذهب الطلبات من نفس المصدر لنفس الخادم دائما.

<note tip> يمنع وضع _ في النطاق لذا استخدمناه كي نؤكد على أن الطلب http://_example_com_loadbalancer طلب داخلي وليس نطاق حقيقي </note>

للمزيد انظر

المزيد من الوثائق

نقاش

niceboy, 2012/03/25 12:26

شكرا جزيلا على هذا المقال المفيد..

ahmed awad, 2012/09/24 13:58

مقاله ممتازه ، قمت بتجربتها علي موقع اختباري ، وهو يعمل حاليا عليه <a href=“http://yiie.net”>yii</a>

lrbnivrpel, 2015/04/24 16:00

إعدادات خادم الويب NginX الفضلى [أعجوبة] lrbnivrpel http://www.g99i74i1i03zi8b3z152i8idlt1dvz38s.org/ [url=http://www.g99i74i1i03zi8b3z152i8idlt1dvz38s.org/]ulrbnivrpel[/url] <a href=“http://www.g99i74i1i03zi8b3z152i8idlt1dvz38s.org/”>alrbnivrpel</a>

ليث العبيدي, 2017/07/09 13:30
ممتاز
أدخل تعليقك:
 
آخر تعديل:: 23 نيسان 2015 الساعة 00:19 (تحرير خارجي)