رفتن به محتوا
LoopX

Nginx چیست؟

توی این درس یاد می‌گیری Nginx (تلفظ: «اِنجین‌اِکس») دقیقاً چیست و چرا تقریباً جلوی هر سایت جدی یک Nginx (یا چیزی شبیه آن) نشسته است. سه نقش اصلی‌اش را با مثال واقعی می‌بینی: وب‌سرور (web server) که فایل‌ها را مستقیم می‌فرستد، reverse proxy که درخواست‌ها را به اپلیکیشن پشتش می‌رساند، و load balancer که بار را بین چند سرور پخش می‌کند. با nginx -v نسخه‌اش را می‌بینی، با curl -I می‌فهمی یک سایت با چه وب‌سروری جواب می‌دهد، و می‌فهمی معماری رویدادمحور (event-driven) Nginx چه فرقی با Apache دارد.

مسئله: اپلیکیشن تو تنها جلوی اینترنت

Section titled “مسئله: اپلیکیشن تو تنها جلوی اینترنت”

فرض کن یک اپ Node.js یا Python نوشته‌ای که روی پورت 3000 گوش می‌دهد. می‌توانی همین را مستقیم روی اینترنت بگذاری؟ از نظر فنی بله، ولی خیلی زود به این‌ها می‌خوری:

  • HTTPS: گواهی، رمزنگاری و تمدیدش را باید خود اپ مدیریت کند.
  • فایل‌های استاتیک: هزاران عکس، CSS و JS؛ اپ تو برای فرستادن هر کدام وقت پردازنده می‌گذارد.
  • چند دامنه روی یک سرور: shop.example.com و blog.example.com هر دو روی پورت ۸۰ و ۴۴۳ می‌آیند؛ کدام اپ جواب بدهد؟
  • بار زیاد: وقتی یک نسخه از اپ کافی نیست و سه نسخه داری، چه کسی درخواست‌ها را تقسیم کند؟
  • محافظت: محدودکردن درخواست‌های زیاد، هدرهای امنیتی، پنهان‌کردن جزئیات اپ.

این‌ها کار اپ تو نیست. کار یک برنامه‌ی تخصصی است که جلوی اپ بنشیند: Nginx.

تشبیه: پذیرش یک ساختمان اداری

Section titled “تشبیه: پذیرش یک ساختمان اداری”

Nginx مثل میز پذیرش یک ساختمان اداری است. همه از یک در ورودی (پورت ۸۰ و ۴۴۳) وارد می‌شوند و اول به پذیرش می‌رسند:

  • اگر فقط یک بروشور می‌خواهند (فایل استاتیک)، پذیرش خودش از قفسه برمی‌دارد و می‌دهد؛ لازم نیست مزاحم کارمندها شود (وب‌سرور).
  • اگر کار واقعی دارند، پذیرش آن‌ها را به اتاق درست می‌فرستد و جواب را برمی‌گرداند؛ مراجع اصلاً نمی‌داند اتاق کجاست (reverse proxy).
  • اگر سه کارمند همان کار را انجام می‌دهند، پذیرش مراجعان را بینشان تقسیم می‌کند تا یکی زیر بار نرود (load balancer).
  • و پذیرش است که کارت شناسایی را چک می‌کند و آدم‌های مشکوک را راه نمی‌دهد (HTTPS، محدودیت درخواست، هدرهای امنیتی).
نقش Nginx چه می‌کند نمونه
وب‌سرور فایل‌ها را مستقیم از دیسک می‌فرستد سایت استاتیک، خروجی build یک اپ React یا Astro، عکس‌ها
reverse proxy درخواست را به یک سرور دیگر (اپ) می‌فرستد و جواب را به کاربر برمی‌گرداند جلوی اپ Node، Python، PHP یا یک کانتینر
load balancer درخواست‌ها را بین چند نسخه از یک اپ پخش می‌کند سه نسخه از API پشت یک آدرس

در عمل یک Nginx معمولاً هر سه کار را همزمان انجام می‌دهد، به‌علاوه‌ی پایان دادن HTTPS (TLS termination)، فشرده‌سازی (gzip)، کش و محدودسازی درخواست‌ها؛ همه‌ی این‌ها را در درس‌های این دوره می‌سازی.

جایگاه Nginx: همه‌ی درخواست‌ها از اینترنت اول به Nginx می‌رسند. فایل‌های استاتیک را خودش از دیسک می‌فرستد و بقیه را (به‌عنوان reverse proxy) به یک یا چند نسخه از اپلیکیشن می‌رساند. اپ‌ها فقط روی شبکه‌ی داخلی گوش می‌دهند و مستقیم در دسترس نیستند.

این درس روی ماشین آزمایشی server (Ubuntu 24.04 با systemd) اجرا شده که Nginx از مخزن خود Ubuntu رویش نصب است؛ نصب را در درس بعد قدم‌به‌قدم می‌بینی. دستورها با کاربر root اجرا شده‌اند؛ روی سرور خودت جلویشان sudo بگذار.

مثال ۱: nginx -v، کدام نسخه؟

Section titled “مثال ۱: nginx -v، کدام نسخه؟”

اولین دستور هر Nginx: نسخه‌اش چیست؟

Terminal window
nginx -v
echo "--- کد خروج: $?"
echo "--- nginx -V: نسخه به‌علاوه‌ی گزینه‌های build (چند خط اول):"
nginx -V 2>&1 | head -n 3
nginx -V 2>&1 | grep -o -- '--with-http_[a-z0-9_]*' | head -n 6
خروجی
nginx version: nginx/1.24.0 (Ubuntu)
--- کد خروج: 0
--- nginx -V: نسخه به‌علاوه‌ی گزینه‌های build (چند خط اول):
nginx version: nginx/1.24.0 (Ubuntu)
built with OpenSSL 3.0.13 30 Jan 2024
TLS SNI support enabled
--with-http_ssl_module
--with-http_stub_status_module
--with-http_realip_module
--with-http_auth_request_module
--with-http_v2_module
--with-http_dav_module
  • nginx -v (v کوچک) فقط نسخه را می‌گوید: 1.24.0، ساخته‌شده برای Ubuntu.
  • nginx -V (V بزرگ) به‌علاوه‌ی نسخه‌ی OpenSSL و همه‌ی گزینه‌هایی که Nginx با آن‌ها ساخته (compile) شده را نشان می‌دهد. وقتی می‌خواهی ببینی یک ماژول (مثلاً http_ssl_module برای HTTPS یا http_v2_module برای HTTP/2) در Nginx تو هست یا نه، اینجا را نگاه می‌کنی.
  • این دستورها خروجی را روی stderr چاپ می‌کنند (برای همین با 2>&1 به grep دادیم).

مثال ۲: Nginx به‌عنوان وب‌سرور

Section titled “مثال ۲: Nginx به‌عنوان وب‌سرور”

Nginx الان در حال اجراست و پوشه‌ی /var/www/html را سرو می‌کند. با curl یک درخواست می‌فرستیم؛ -i هدرهای پاسخ را هم نشان می‌دهد:

Terminal window
systemctl is-active nginx
curl -s -i http://localhost/ | head -n 12
خروجی
active
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Sun, 04 Oct 2026 12:41:56 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Sun, 04 Oct 2026 12:38:27 GMT
Connection: keep-alive
ETag: "6ac248c3-267"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>

خط اول وضعیت (status) است: HTTP/1.1 200 OK. بعد هدرها: Server: nginx/1.24.0 (Ubuntu) می‌گوید چه کسی جواب داد؛ Content-Type نوع فایل؛ Content-Length اندازه؛ Last-Modified و ETag برای کش مرورگر. بعد از یک خط خالی، بدنه (body) شروع می‌شود: همان صفحه‌ی «Welcome to nginx!».

حالا فایل خودمان را می‌گذاریم. وب‌سرور یعنی همین: یک فایل در پوشه، یک آدرس در مرورگر.

Terminal window
echo '<h1>Hello from LoopX</h1>' > /var/www/html/hello.html
curl -s http://localhost/hello.html
echo "--- فایلی که نیست:"
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost/nothing.html
rm /var/www/html/hello.html
خروجی
<h1>Hello from LoopX</h1>
--- فایلی که نیست:
HTTP 404

آدرس /hello.html به فایل /var/www/html/hello.html نگاشت (map) شد. برای فایل ناموجود، Nginx کد 404 داد. (-w "%{http_code}" به curl می‌گوید فقط کد وضعیت را چاپ کند.)

مثال ۳: curl -I، هر سایت با چه وب‌سروری جواب می‌دهد؟

Section titled “مثال ۳: curl -I، هر سایت با چه وب‌سروری جواب می‌دهد؟”

curl -I (I بزرگ) فقط هدرها را می‌گیرد (یک درخواست HEAD). هدر Server معمولاً نام وب‌سرور را لو می‌دهد. برای مقایسه، یک وب‌سرور ساده‌ی دیگر هم بالا می‌آوریم: سرور داخلی پایتون:

Terminal window
mkdir -p /srv/lx-py && echo "python here" > /srv/lx-py/index.html
(cd /srv/lx-py && exec python3 -m http.server 9001 --bind 127.0.0.1) > /dev/null 2>&1 &
sleep 1
echo "=== Nginx:"
curl -s -I http://localhost/ | grep -iE '^(HTTP|server)'
echo "=== Python http.server:"
curl -s -I http://127.0.0.1:9001/ | grep -iE '^(HTTP|server)'
خروجی
=== Nginx:
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
=== Python http.server:
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.3

روی سایت‌های واقعی هم همین را امتحان کن (این بخش تمرین اول درس است):

روی سیستم خودت امتحان کن
curl -sI https://www.wikipedia.org | grep -i '^server'
curl -sI https://github.com | grep -i '^server'
curl -sI https://www.digikala.com | grep -i '^server'

مثال ۴: Nginx به‌عنوان reverse proxy

Section titled “مثال ۴: Nginx به‌عنوان reverse proxy”

حالا همان سرور پایتون (روی 127.0.0.1:9001، که از بیرون دیده نمی‌شود) را پشت Nginx می‌گذاریم. یک فایل تنظیمات کوچک می‌سازیم که هر درخواست به پورت 8081 را به پایتون بفرستد (جزئیات نحو را در درس‌های بعد کامل یاد می‌گیری):

/etc/nginx/conf.d/lx-proxy.conf
server {
listen 8081;
location / {
proxy_pass http://127.0.0.1:9001;
}
}
Terminal window
nginx -t
systemctl reload nginx
sleep 1
curl -s -i http://localhost:8081/ | grep -iE '^(HTTP|server)|python'
خروجی
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
python here
  • nginx -t تنظیمات را قبل از اعمال بررسی می‌کند (درس ساختار تنظیمات) و systemctl reload nginx آن را بدون قطع سرویس اعمال می‌کند.
  • جواب (python here) از پایتون آمد، ولی هدر Server حالا nginx است: کاربر فقط Nginx را می‌بیند. Nginx هدر Server اپ پشتش را به‌صورت پیش‌فرض پنهان می‌کند و هدر خودش را می‌گذارد.
  • پایتون فقط روی 127.0.0.1 گوش می‌دهد؛ یعنی از بیرون سرور قابل دسترس نیست و فقط Nginx به آن می‌رسد. این الگوی استاندارد است.

مثال ۵: Nginx به‌عنوان load balancer

Section titled “مثال ۵: Nginx به‌عنوان load balancer”

دو نسخه‌ی «اپ» (دو سرور پایتون با محتوای متفاوت تا ببینیم کدام جواب داد) و یک Nginx که بینشان پخش کند:

/etc/nginx/conf.d/lx-lb.conf
upstream lx_app {
zone lx_app 64k;
server 127.0.0.1:9011;
server 127.0.0.1:9012;
}
server {
listen 8082;
location / {
proxy_pass http://lx_app;
}
}
Terminal window
for port in 9011 9012; do
mkdir -p /srv/lx-app-$port
echo "response from app on port $port" > /srv/lx-app-$port/index.html
(cd /srv/lx-app-$port && exec python3 -m http.server $port --bind 127.0.0.1) > /dev/null 2>&1 &
done
sleep 1
nginx -t 2>&1 | tail -n 1
systemctl reload nginx
sleep 1
for i in 1 2 3 4; do
curl -s http://localhost:8082/
done
خروجی
nginx: configuration file /etc/nginx/nginx.conf test is successful
response from app on port 9011
response from app on port 9012
response from app on port 9011
response from app on port 9012

بلوک upstream یک گروه از سرورها با یک اسم (lx_app) تعریف می‌کند و proxy_pass به آن گروه می‌فرستد. Nginx به‌صورت پیش‌فرض به نوبت (round robin) پخش می‌کند: ۹۰۱۱، ۹۰۱۲، ۹۰۱۱، ۹۰۱۲. خط zone یک حافظه‌ی مشترک می‌سازد تا همه‌ی worker ها یک نوبت مشترک داشته باشند؛ بدون آن هر worker نوبت خودش را نگه می‌دارد و ترتیب کمی به هم می‌ریزد (در درس load balancing هر دو حالت را می‌بینی). اگر یکی از نسخه‌ها از کار بیفتد، Nginx درخواست را به بقیه می‌دهد:

Terminal window
pkill -f 'http.server 9012'
sleep 1
for i in 1 2 3; do
curl -s -o /dev/null -w "HTTP %{http_code} " http://localhost:8082/
curl -s http://localhost:8082/
done
خروجی
HTTP 200 response from app on port 9011
HTTP 200 response from app on port 9011
HTTP 200 response from app on port 9011

با اینکه یکی از دو نسخه خاموش است، همه‌ی درخواست‌ها با 200 جواب گرفتند (از نسخه‌ی سالم). جزئیات (least_conn، max_fails و …) در درس load balancing.

مثال ۶: سریع، با حافظه‌ی کم

Section titled “مثال ۶: سریع، با حافظه‌ی کم”

یکی از دلیل‌های محبوبیت Nginx این است که با منابع کم تعداد زیادی اتصال همزمان را جواب می‌دهد. با ابزار ab (ApacheBench) دو هزار درخواست با ۵۰ اتصال همزمان می‌فرستیم؛ یک بار به Nginx و یک بار به سرور ساده‌ی پایتون، هر دو برای یک فایل کوچک:

Terminal window
cp /srv/lx-py/index.html /var/www/html/py.html
echo "=== Nginx:"
ab -q -n 2000 -c 50 http://127.0.0.1/py.html 2>&1 | grep -E 'Requests per second|Failed requests|Time per request.*mean\)$'
echo "=== Python http.server:"
ab -q -n 2000 -c 50 http://127.0.0.1:9001/index.html 2>&1 | grep -E 'Requests per second|Failed requests|Time per request.*mean\)$'
rm /var/www/html/py.html
خروجی
=== Nginx:
Failed requests: 0
Requests per second: 19389.24 [#/sec] (mean)
Time per request: 2.579 [ms] (mean)
=== Python http.server:
Failed requests: 0
Requests per second: 71.97 [#/sec] (mean)
Time per request: 694.763 [ms] (mean)

اعداد دقیق به سخت‌افزار بستگی دارند و روی سیستم تو فرق می‌کنند، ولی فاصله‌ی چند برابری (اینجا بیش از صد برابر) همیشه هست: سرور پایتون برای آزمایش ساخته شده، Nginx برای همین کار. (این مقایسه منصفانه نیست و قرار هم نیست باشد؛ نکته این است که فایل استاتیک را به Nginx بسپار، نه به اپ.)

پشت پرده: master، worker و حلقه‌ی رویداد

Section titled “پشت پرده: master، worker و حلقه‌ی رویداد”

Nginx یک برنامه‌ی تک‌پروسه نیست. یک پروسه‌ی master دارد که تنظیمات را می‌خواند، پورت‌ها را باز می‌کند و چند پروسه‌ی worker می‌سازد که کار واقعی (جواب دادن به درخواست‌ها) را انجام می‌دهند:

Terminal window
ps -o pid,ppid,user,cmd -C nginx
echo "--- تعداد worker ها در تنظیمات:"
grep -E '^\s*worker_(processes|connections)' /etc/nginx/nginx.conf
echo "--- تعداد هسته‌های CPU: $(nproc)"
خروجی
PID PPID USER CMD
14851 1 root nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
14904 14851 www-data nginx: worker process
14905 14851 www-data nginx: worker process
14906 14851 www-data nginx: worker process
14909 14851 www-data nginx: worker process
--- تعداد worker ها در تنظیمات:
worker_processes auto;
worker_connections 768;
--- تعداد هسته‌های CPU: 4
  • master با کاربر root اجرا می‌شود (چون باز کردن پورت ۸۰ زیر ۱۰۲۴ دسترسی root می‌خواهد) ولی خودش به درخواست‌ها جواب نمی‌دهد.
  • worker ها با کاربر کم‌دسترسی www-data اجرا می‌شوند؛ اگر کسی از راه یک باگ worker را در اختیار بگیرد، دسترسی root ندارد.
  • worker_processes تعداد worker هاست (auto، پیش‌فرض Ubuntu، یعنی یکی به ازای هر هسته‌ی CPU؛ اینجا ۴ هسته و ۴ worker) و worker_connections حداکثر اتصال همزمان هر worker.

راز سرعت در مدل رویدادمحور است. Apache در حالت کلاسیکش برای هر اتصال یک پروسه یا thread جدا اختصاص می‌دهد؛ اگر ده هزار کاربر با اینترنت کند وصل باشند، ده هزار پروسه یا thread منتظر می‌مانند و هر کدام حافظه می‌خورند. هر worker Nginx یک حلقه‌ی رویداد (event loop) است: از سیستم‌عامل (با epoll در لینوکس) می‌پرسد «کدام اتصال‌ها الان داده‌ی آماده دارند؟» و فقط به همان‌ها رسیدگی می‌کند؛ بقیه هیچ منبعی جز چند کیلوبایت حافظه نمی‌گیرند.

دو مدل رسیدگی به اتصال‌ها. در مدل یک thread به ازای هر اتصال (Apache کلاسیک با prefork یا worker)، هر اتصال منتظر یک thread یا پروسه را اشغال می‌کند. در مدل رویدادمحور Nginx، چند worker (معمولاً به تعداد هسته‌ها) هزاران اتصال را با یک حلقه‌ی رویداد مدیریت می‌کنند.

وقتی تنظیمات را با reload عوض می‌کنی، master تنظیمات تازه را می‌خواند، worker های جدید می‌سازد و به worker های قدیمی می‌گوید درخواست‌های در حال اجرا را تمام کنند و بروند. برای همین reload سرویس را قطع نمی‌کند:

Terminal window
echo "قبل از reload: $(pgrep -d ' ' -f 'nginx: worker')"
systemctl reload nginx
sleep 1
echo "بعد از reload: $(pgrep -d ' ' -f 'nginx: worker')"
echo "master عوض شد؟ $(pgrep -f 'nginx: master')"
خروجی
قبل از reload: 14904 14905 14906 14909
بعد از reload: 16951 16952 16953 16954
master عوض شد؟ 14851

شماره‌ی پروسه‌ی worker ها عوض شد (worker های تازه) ولی master همان ماند.

Nginx در برابر Apache:

Nginx Apache httpd
مدل اتصال رویدادمحور (چند worker، هر کدام هزاران اتصال) ماژول‌های MPM: prefork (پروسه به ازای اتصال)، worker، event
فایل استاتیک و اتصال‌های زیاد بسیار سریع با حافظه‌ی کم خوب، با حافظه‌ی بیشتر
تنظیمات فقط فایل‌های اصلی؛ بعد از تغییر reload به‌علاوه‌ی .htaccess در هر پوشه (بدون reload، ولی کندتر)
اجرای PHP به PHP-FPM می‌فرستد (proxy) می‌تواند با mod_php داخل خودش اجرا کند
کاربرد رایج امروز reverse proxy، load balancer، سایت استاتیک میزبانی اشتراکی و برنامه‌هایی که .htaccess می‌خواهند

دستورهای این درس:

دستور کار
nginx -v نسخه
nginx -V نسخه + گزینه‌ها و ماژول‌های build
nginx -t آزمایش تنظیمات (درس سوم)
systemctl reload nginx اعمال تنظیمات بدون قطع سرویس
curl -i URL پاسخ با هدرها
curl -I URL فقط هدرها (درخواست HEAD)
curl -s -o /dev/null -w "%{http_code}" URL فقط کد وضعیت
ps -o pid,user,cmd -C nginx پروسه‌های master و worker

۱) فکر کردن که Nginx «زبان برنامه‌نویسی» یا «فریم‌ورک» است

Section titled “۱) فکر کردن که Nginx «زبان برنامه‌نویسی» یا «فریم‌ورک» است”

Nginx کد اپ تو (Python، PHP، Node) را اجرا نمی‌کند؛ فایل می‌فرستد یا درخواست را به برنامه‌ی دیگری می‌رساند. برای PHP هم Nginx درخواست را به PHP-FPM می‌دهد. راه‌حل: اپ را جدا اجرا کن (سرویس systemd یا کانتینر) و Nginx را جلویش بگذار.

۲) دو برنامه روی یک پورت

Section titled “۲) دو برنامه روی یک پورت”
Terminal window
python3 -m http.server 80 --bind 0.0.0.0 2>&1 | tail -n 1
خروجی
OSError: [Errno 98] Address already in use

Nginx پورت ۸۰ را گرفته و برنامه‌ی دیگری نمی‌تواند همان را بگیرد (Address already in use). برعکسش هم رایج است: Apache نصب است و Nginx بالا نمی‌آید. راه‌حل: با ss -tlnp (درس پورت‌ها در دوره‌ی لینوکس) ببین چه کسی پورت را گرفته؛ اپ‌ها را روی پورت‌های داخلی (مثل 127.0.0.1:3000) بگذار و فقط Nginx روی ۸۰ و ۴۴۳ باشد.

۳) اعتماد کامل به هدر Server

Section titled “۳) اعتماد کامل به هدر Server”

هدر Server را هر کسی می‌تواند عوض یا پنهان کند (درس هدرهای امنیتی: server_tokens off). راه‌حل: آن را یک سرنخ بدان، نه مدرک.

Terminal window
echo "فقط stdout:"
nginx -V 2>/dev/null | grep -c ssl
echo "--- با 2>&1:"
nginx -V 2>&1 | grep -c ssl
خروجی
فقط stdout:
0
--- با 2>&1:
1

وقتی فقط stdout به grep برسد (اینجا stderr را دور ریختیم؛ بدون 2>/dev/null همان متن روی صفحه می‌آید ولی باز به grep نمی‌رسد)، شمارش ۰ است، چون nginx -V روی stderr می‌نویسد و pipe فقط stdout را می‌برد. راه‌حل: 2>&1.

✎ تمرینآسان

تمرین اصلی درس: با curl -sI بررسی کن سه سایت معروف (مثلاً ویکی‌پدیا، GitHub و یک سایت ایرانی که زیاد استفاده می‌کنی) با چه وب‌سروری جواب می‌دهند. اگر هدر Server نبود یا اسم CDN بود، دنبال هدرهای دیگری بگرد که سرنخ می‌دهند (مثل x-powered-by، via یا x-cache).

دیدن جواب
دستورها (روی سیستم خودت)
for site in https://www.wikipedia.org https://github.com https://www.digikala.com; do
echo "== $site"
curl -sI "$site" | grep -iE '^(server|via|x-powered-by|x-cache):'
done

خروجی به زمان و جای تو بستگی دارد (و در محیط آزمایشی ما بدون اینترنت مستقیم اجرا نشد). چیزهایی که معمولاً می‌بینی: بعضی سایت‌ها nginx یا nginx/1.x، بعضی اسم CDN یا وب‌سرور سفارشی‌شان (مثل cloudflare، ATS، envoy)، و بعضی اصلاً هدر Server نمی‌فرستند. هدر via یعنی پاسخ از یک proxy گذشته است. روی سرور خودمان همین کار را واقعاً اجرا کردیم:

Terminal window
for url in http://localhost/ http://localhost:8081/ http://127.0.0.1:9001/; do
printf '%-26s ' "$url"
curl -sI "$url" | grep -i '^server' | tr -d '\r'
done
خروجی
http://localhost/ Server: nginx/1.24.0 (Ubuntu)
http://localhost:8081/ Server: nginx/1.24.0 (Ubuntu)
http://127.0.0.1:9001/ Server: SimpleHTTP/0.6 Python/3.12.3
✎ تمرینمتوسط

فایل /var/www/html/about.html بساز و با curl -i سه چیز را از هدرهای پاسخ پیدا کن: نوع محتوا، اندازه‌ی بدنه به بایت، و تاریخ آخرین تغییر. بعد فایل را عوض کن و ببین کدام هدرها عوض شدند. در پایان فایل را پاک کن و کد وضعیت را دوباره بگیر.

دیدن جواب
Terminal window
printf '<h1>About LoopX</h1>\n' > /var/www/html/about.html
touch -d '2026-01-01 10:00:00' /var/www/html/about.html
curl -s -i http://localhost/about.html | grep -iE '^(content-type|content-length|last-modified|etag)'
echo "--- بعد از تغییر:"
printf '<h1>About LoopX</h1>\n<p>Practical lessons in Persian.</p>\n' > /var/www/html/about.html
curl -s -i http://localhost/about.html | grep -iE '^(content-length|last-modified|etag)'
rm /var/www/html/about.html
echo "--- بعد از حذف:"
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost/about.html
خروجی
Content-Type: text/html
Content-Length: 21
Last-Modified: Thu, 01 Jan 2026 10:00:00 GMT
ETag: "695645a0-15"
--- بعد از تغییر:
Content-Length: 58
Last-Modified: Sun, 04 Oct 2026 12:42:31 GMT
ETag: "6ac249b7-3a"
--- بعد از حذف:
HTTP 404

Content-Type: text/html از پسوند .html آمده (Nginx از جدول mime.types می‌خواند). Content-Length دقیقاً اندازه‌ی فایل است و Last-Modified زمان تغییر فایل روی دیسک. ETag از زمان تغییر و اندازه ساخته می‌شود؛ پس با تغییر فایل هر سه عوض شدند. مرورگر با همین‌ها می‌فهمد نسخه‌ی کش‌شده‌اش هنوز معتبر است یا نه (درس عملکرد).

✎ تمرینسخت

یک reverse proxy روی پورت 8083 بساز که درخواست‌ها را به سه نسخه‌ی پایتون (پورت‌های ۹۰۲۱ تا ۹۰۲۳) پخش کند. با یک حلقه ۶ درخواست بفرست و بشمار هر نسخه چند بار جواب داد. بعد یک نسخه را خاموش کن و دوباره ۶ درخواست بفرست. در پایان همه را پاک کن.

دیدن جواب
Terminal window
for port in 9021 9022 9023; do
mkdir -p /srv/lx-app-$port
echo "app-$port" > /srv/lx-app-$port/index.html
(cd /srv/lx-app-$port && exec python3 -m http.server $port --bind 127.0.0.1) > /dev/null 2>&1 &
done
cat > /etc/nginx/conf.d/lx-three.conf <<'EOF'
upstream lx_three {
zone lx_three 64k;
server 127.0.0.1:9021;
server 127.0.0.1:9022;
server 127.0.0.1:9023;
}
server {
listen 8083;
location / {
proxy_pass http://lx_three;
}
}
EOF
sleep 1
nginx -t 2>&1 | tail -n 1 && systemctl reload nginx
sleep 1
echo "=== سه نسخه:"
for i in $(seq 6); do curl -s http://localhost:8083/; done | sort | uniq -c
pkill -f 'http.server 9022'
sleep 1
echo "=== بعد از خاموشی 9022:"
for i in $(seq 6); do curl -s http://localhost:8083/; done | sort | uniq -c
rm -f /etc/nginx/conf.d/lx-three.conf
systemctl reload nginx
sleep 1
pkill -f 'http.server 902'
خروجی
nginx: configuration file /etc/nginx/nginx.conf test is successful
=== سه نسخه:
2 app-9021
2 app-9022
2 app-9023
=== بعد از خاموشی 9022:
3 app-9021
3 app-9023

با سه نسخه و zone مشترک، هر کدام دقیقاً ۲ بار (round robin). بعد از خاموشی یکی، دو نسخه‌ی باقی‌مانده هر کدام ۳ بار جواب دادند و هیچ درخواستی شکست نخورد. sort | uniq -c (درس pipe ها) شمارش را انجام داد.

⚡ بررسی سریع

اپ Node تو روی 127.0.0.1:3000 اجرا می‌شود و Nginx روی پورت ۸۰ درخواست‌ها را به آن می‌فرستد. Nginx در این حالت چه نقشی دارد؟

؟ آزمونک
  1. تفاوت nginx -v و nginx -V چیست؟

  2. چرا پروسه‌ی master Nginx با root و worker ها با www-data اجرا می‌شوند؟

  3. راز اینکه Nginx با چند worker هزاران اتصال همزمان را با حافظه‌ی کم جواب می‌دهد چیست؟

  4. در یک upstream با دو سرور، یکی خاموش می‌شود. با تنظیمات پیش‌فرض چه می‌شود؟

  5. nginx -V | grep ssl هیچ چیزی پیدا نمی‌کند ولی خروجی روی صفحه هست. چرا؟

  6. چرا systemctl reload nginx سرویس را قطع نمی‌کند؟

  • Nginx برنامه‌ای است که جلوی سایت‌ها و اپ‌ها می‌نشیند: وب‌سرور (فایل از دیسک)، reverse proxy (رساندن درخواست به اپ و برگرداندن جواب)، load balancer (پخش بین چند نسخه)؛ به‌علاوه‌ی HTTPS، فشرده‌سازی، کش و محدودسازی.
  • اپ‌ها روی پورت‌های داخلی (127.0.0.1:3000) گوش می‌دهند و فقط Nginx روی ۸۰ و ۴۴۳ است.
  • nginx -v نسخه، nginx -V ماژول‌ها (هر دو روی stderr). curl -i پاسخ با هدر و curl -I فقط هدر؛ هدر Server سرنخ است، نه مدرک.
  • معماری: یک master (root، تنظیمات و پورت‌ها) و چند worker (www-data، کار واقعی)؛ هر worker یک حلقه‌ی رویداد با epoll است. reload worker ها را بدون قطع سرویس عوض می‌کند.
  • در برابر Apache: Nginx برای اتصال‌های زیاد، فایل استاتیک و proxy سبک‌تر است؛ Apache با .htaccess و mod_php در میزبانی اشتراکی هنوز رایج است.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
nginx -vنسخه‌ی Nginx
nginx -V 2>&1 | grep -o -- "--with-[a-z0-9_]*"ماژول‌های build شده
curl -i http://localhost/پاسخ کامل با هدرها
curl -sI https://site | grep -i ^serverوب‌سرور یک سایت (فقط هدرها)
curl -s -o /dev/null -w "%{http_code}\n" URLفقط کد وضعیت
nginx -t && systemctl reload nginxآزمایش و اعمال تنظیمات
proxy_pass http://127.0.0.1:9001;reverse proxy به یک اپ
upstream app { server a; server b; }گروه سرورها برای load balancing
ps -o pid,user,cmd -C nginxmaster و worker ها
ab -n 2000 -c 50 URLآزمایش بار ساده