سرور

الگوریتم‌های انتخاب بلاک Server و Location در Nginx

مقدمه

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

این راهنما توضیح می‌دهد که Nginx چگونه تصمیم می‌گیرد کدام بلاک‌های پیکربندی، درخواست‌های ورودی را مدیریت کنند. این بلاک‌ها که server و location نامیده می‌شوند، هسته اصلی نحوه نگاشت دامنه‌ها، آدرس‌های IP و URIهای درخواست به محتوای خاص یا اپلیکیشن‌های بک‌اند را تشکیل می‌دهند. با درک نحوه ارزیابی این بلاک‌ها توسط Nginx، می‌توانید پیکربندی‌هایی بنویسید که رفتار قابل پیش‌بینی دارند، از تداخل‌های مسیریابی جلوگیری کنید و دیباگ را ساده‌تر کنید.

در پایان این راهنما، درک خواهید کرد که Nginx چگونه بلاک‌های server و location را تفسیر و اولویت‌بندی می‌کند، توالی ارزیابی‌هایی که هنگام رسیدن یک درخواست رخ می‌دهند و قوانین، مادیفایرها (Modifier) و استثنائاتی که تعیین می‌کنند از کدام پیکربندی استفاده شود. این دانش به شما کمک می‌کند پیکربندی‌های تمیز و قابل نگهداری طراحی کنید و مشکلات مسیریابی را با اطمینان عیب‌یابی کنید.

نکات کلیدی

  • اولویت دایرکتیو listen: Nginx ابتدا بلاک‌های server را بر اساس دایرکتیو listen فیلتر می‌کند. قبل از ارزیابی هر نام دامنه‌ای، مشخص بودن آدرس IP و پورت را بررسی می‌کند. بلاکی که روی یک IP مشخص گوش می‌دهد (مثلاً 192.168.1.10:80) بر بلاکی که روی 0.0.0.0:80 گوش می‌دهد اولویت دارد.
  • ترتیب ارزیابی server_name: وقتی چند بلاک server همان IP و پورت را تعریف کرده‌اند، Nginx هدر Host را با دایرکتیوهای server_name به‌ترتیب سخت‌گیرانه‌ای مقایسه می‌کند: تطابق دقیق، وایلدکارت ابتدایی (مثل *.example.com)، وایلدکارت انتهایی (مثل www.example.*) و در نهایت عبارات باقاعده.
  • سرور پیش‌فرض به‌عنوان جایگزین: اگر هدر Host با هیچ server_name تعریف‌شده‌ای مطابقت نداشته باشد، Nginx درخواست را به سرور پیش‌فرض برای آن ترکیب IP/پورت هدایت می‌کند. این سرور یا اولین بلاک تعریف‌شده در پیکربندی است یا بلاکی که صریحاً با پارامتر default_server مشخص شده.
  • سلسله‌مراتب مادیفایرهای Location: بلاک‌های location از مادیفایرهای خاصی برای تعیین رفتار تطبیق استفاده می‌کنند. مادیفایر = تطابق دقیق را بررسی می‌کند، ~ عبارت باقاعده حساس به بزرگی و کوچکی حروف است، ~* عبارت باقاعده بدون حساسیت به بزرگی و کوچکی حروف است و ^~ تطبیق پیشوندی انجام می‌دهد که بررسی‌های regex بعدی را غیرفعال می‌کند.
  • Regex معمولاً بر پیشوند غلبه می‌کند: به‌طور پیش‌فرض، Nginx تطابق‌های عبارت باقاعده را به تطابق‌های پیشوندی استاندارد ترجیح می‌دهد؛ حتی اگر تطابق پیشوندی طولانی‌تر باشد. ابتدا همه locationهای پیشوندی را برای پیدا کردن بلندترین آن‌ها ارزیابی می‌کند؛ اما اگر بعداً یک تطابق regex پیدا شود، به آن سوییچ می‌کند.
  • کلید قطع‌کننده ^~: مادیفایر ^~ ابزاری قدرتمند برای کنترل کارایی و منطق است. اگر بلندترین location پیشوندیِ مطابقت‌یافته از ^~ استفاده کند، Nginx فوراً جستجو را متوقف و همه locationهای عبارت باقاعده را نادیده می‌گیرد و از همان بلاک پیشوندی برای سرو کردن درخواست استفاده می‌کند.
  • کارایی تطابق دقیق (=): استفاده از مادیفایر = تطابق رشته‌ای دقیق با URI را الزامی می‌کند. اگر پیدا شود، Nginx جستجو را بلافاصله پایان می‌دهد. این کارآمدترین روش برای مدیریت URIهای خاص و پرتکرار است.
  • ری‌دایرکت‌های داخلی جستجو را ری‌استارت می‌کنند: دایرکتیوهایی مانند index، try_files، rewrite و error_page می‌توانند ری‌دایرکت داخلی ایجاد کنند. این کار باعث می‌شود Nginx الگوریتم انتخاب location را از ابتدا با URI جدید ری‌استارت کند؛ که به‌طور بالقوه با بلاک location متفاوتی از درخواست اصلی تطابق می‌یابد.

چرا انتخاب Server و Location مهم است

هر درخواستی که به سرور Nginx می‌رسد، از یک فرایند ارزیابی دقیق پیروی می‌کند. درک نحوه انتخاب بلاک server و location مناسب، برای حفظ رفتار قابل پیش‌بینی و اجتناب از خطاهای مسیریابی ضروری است. وقتی چند بلاک هم‌پوشانی دارند یا الگوهای مشابهی دارند، Nginx ترتیب ارزیابی سخت‌گیرانه‌ای را برای تصمیم‌گیری اینکه کدامیک درخواست را مدیریت کند اعمال می‌کند. بدون درک روشن این فرایند، حتی تغییرات کوچک پیکربندی می‌توانند به نتایج غیرمنتظره منجر شوند.

یک مثال رایج، وقتی است که دو بلاک location با الگوی URI مشابهی تطابق می‌یابند. اگر یکی از عبارت باقاعده استفاده کند و دیگری از پیشوند، ترتیب ظاهرشان و مادیفایرهایی که استفاده می‌کنند می‌توانند کاملاً نتیجه را تغییر دهند. به همین ترتیب، دو بلاک server که روی همان پورت گوش می‌دهند اما با نام‌های دامنه متفاوت پیکربندی شده‌اند، اگر دایرکتیو server_name درست تعریف نشده باشد ممکن است باعث مسیریابی اشتباه درخواست‌ها شوند. این تفاوت‌های ظریف اغلب برای مدیرانی که مطمئن نیستند Nginx در زمان اجرا کدام بلاک را انتخاب می‌کند، سردرگمی ایجاد می‌کند.

دانستن نحوه کار فرایند انتخاب، آن عدم قطعیت را از بین می‌برد. به شما اجازه می‌دهد فایل‌های پیکربندی‌ای بسازید که هم کارآمد و هم خوانا هستند. می‌توانید با اطمینان پیش‌بینی کنید که Nginx چگونه هر دایرکتیو را تفسیر، تطابق‌ها را اولویت‌بندی و استثنائات ایجادشده توسط دایرکتیوهایی مثل rewrite یا try_files را مدیریت می‌کند.

فرایند انتخاب کامل را می‌توان به سه فاز اصلی تقسیم کرد:

  • انتخاب بلاک server: Nginx تعیین می‌کند کدام بلاک server درخواست را مدیریت کند؛ با مقایسه آدرس IP، پورت و هدر Host کلاینت با همه دایرکتیوهای listen و server_name تعریف‌شده.
  • انتخاب بلاک location: درون سرور انتخاب‌شده، Nginx خاص‌ترین بلاک location را با تست URI درخواست در برابر همه تطابق‌های موجود شناسایی می‌کند.
  • ری‌دایرکت‌های داخلی: اگر ری‌دایرکت داخلی به‌دلیل دایرکتیوهایی مانند index، try_files، rewrite یا error_page رخ دهد، Nginx جستجوی location را در همان زمینه سرور ری‌استارت می‌کند تا هندلر نهایی را پیدا کند.

این فازها در کنار هم تضمین می‌کنند هر درخواست به‌شکل سازگار و قابل پیش‌بینی به مقصد درست هدایت شود. درک محکم این فرایند، پایه ساخت پیکربندی‌های قابل اطمینان و قابل نگهداری Nginx است.

درک بلاک‌های پیکربندی Nginx

Nginx پیکربندی‌اش را به سلسله‌مراتبی از بلاک‌ها سازمان‌دهی می‌کند که تعیین می‌کنند درخواست‌ها چگونه پردازش شوند. هر بلاک رفتار خاصی را برای یک زمینه (Context) تعریف می‌کند؛ مثلاً کدام پورت گوش داده شود، مسیرهای فایل خاص چگونه مدیریت شوند یا چه زمانی ترافیک به سرویس دیگری ارسال شود. درک نحوه کار این بلاک‌ها با هم، اولین قدم برای تفسیر منطق مدیریت درخواست Nginx است.

نمای کلی بلاک‌های Server

بلاک server یک سرور مجازی (Virtual Server) را تعریف می‌کند که درخواست‌ها را بر اساس پارامترهای شبکه خاص مدیریت می‌کند. هر بلاک معمولاً شامل دایرکتیوهایی است که ترکیب آدرس IP، پورت و نام دامنه‌ای که به آن پاسخ می‌دهد را مشخص می‌کنند. بلاک‌های server اغلب برای هاست کردن چندین دامنه یا اپلیکیشن روی همان instance از Nginx استفاده می‌شوند.

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

server {
    listen 80;
    server_name example.com;
    root /var/www/example;
}

در این مثال، Nginx به درخواست‌های HTTP ارسالی به پورت ۸۰ برای دامنه example.com پاسخ می‌دهد. می‌توانید چند بلاک server را درون یک فایل پیکربندی منفرد تعریف کنید؛ که به Nginx اجازه می‌دهد بسته به هدر Host و آدرس مقصد درخواست، محتوای متفاوتی سرو کند.

نمای کلی بلاک‌های Location

بلاک location داخل بلاک server قرار می‌گیرد و نحوه پردازش درخواست‌های URIهای خاص را کنترل می‌کند. URI بخشی از درخواست است که بعد از نام دامنه یا آدرس IP می‌آید. بلاک‌های location می‌توانند تعیین کنند فایل‌های استاتیک چگونه سرو شوند، چه زمانی قوانین کش اعمال شوند یا چه زمانی ترافیک به سرورهای upstream فوروارد شود.

مثلاً:

location /images/ {
    root /var/www/static;
}

این بلاک درخواست‌هایی برای هر URI که با /images/ شروع شود را مدیریت می‌کند. بلاک‌های location انعطاف‌پذیرند و می‌توانند URIها را با رشته‌های دقیق، پیشوندها یا عبارات باقاعده تطبیق دهند. روش تطبیقی که انتخاب می‌کنید تعیین می‌کند Nginx چگونه درخواست‌ها را اولویت‌بندی و پردازش کند.

Locationهای تودرتو و وراثت

Nginx اجازه می‌دهد بلاک‌های پیکربندی تودرتو باشند. این ساختار سلسله‌مراتبی امکان تعریف قوانین عمومی در سطح بالاتر و سپس بازنویسی آن‌ها در زمینه‌های خاص‌تر را فراهم می‌کند. مثلاً یک بلاک location می‌تواند دایرکتیوها را از بلاک server والدش به ارث ببرد اما در صورت لزوم آن‌ها را بازتعریف کند.

وقتی درخواستی پردازش می‌شود، Nginx دایرکتیوها را از زمینه اصلی (Main Context)، بلاک server و در نهایت بلاک location مطابقت‌یافته اعمال می‌کند. دایرکتیوهایی که در عمق سلسله‌مراتب قرار دارند اولویت دارند؛ مگر اینکه صریحاً برای ادغام یا وراثت طراحی شده باشند. این رویکرد لایه‌ای به حفظ رفتار سازگار کمک می‌کند و در عین حال کنترل دقیق در جاهای لازم را ممکن می‌سازد.

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

  • index
  • try_files
  • rewrite
  • error_page

درک اینکه این پرش‌ها کِی رخ می‌دهند حیاتی است؛ چون می‌توانند تغییر دهند کدام بلاک location در نهایت یک درخواست را مدیریت کند. این ارزیابی‌های داخلی بعداً وقتی الگوریتم انتخاب location را بررسی می‌کنیم، با جزئیات بحث خواهند شد.

Nginx چگونه بلاک Server را انتخاب می‌کند

قبل از اینکه Nginx بتواند درخواستی را پردازش کند، باید تصمیم بگیرد کدام بلاک server آن را مدیریت کند. هر درخواست با همه بلاک‌های server موجود در پیکربندی مقایسه می‌شود و Nginx از مجموعه‌ای معین از بررسی‌ها برای پیدا کردن خاص‌ترین تطابق استفاده می‌کند. این فرایند انتخاب بر اساس مقادیر آدرس IP، پورت و هدر Host درخواست است؛ که Nginx آن‌ها را با دایرکتیوهای listen و server_name هر بلاک مقایسه می‌کند.

تطبیق دایرکتیو listen

اولین مرحله، تطبیق آدرس IP و پورت درخواست با مقادیر تعریف‌شده در هر دایرکتیو listen است. دایرکتیو listen به Nginx می‌گوید بلاک server به کدام ترکیب آدرس IP و پورت پاسخ می‌دهد. مثلاً:

server {
    listen 80;
    server_name example.com;
}

درخواستی که به پورت ۸۰ روی هاست ارسال شود با این بلاک به‌عنوان کاندیدا تطبیق می‌یابد.

اگر چند بلاک server دایرکتیوهای listen سازگاری داشته باشند، Nginx به مرحله ارزیابی بعدی می‌رود؛ اما ابتدا همه مقادیر listen را به جفت‌های کامل IP-و-پورت نرمال می‌کند. این نرمال‌سازی به تضمین مقایسه سازگار کمک می‌کند.

Nginx هنگام تفسیر دایرکتیوهای listen این منطق را اعمال می‌کند:

  • بلاکی بدون دایرکتیو listen به‌عنوان 0.0.0.0:80 رفتار می‌کند (یا 0.0.0.0:8080 اگر Nginx با کاربر غیر root اجرا شود).
  • آدرس IP بدون پورت، مانند listen 111.111.111.111; به‌عنوان 111.111.111.111:80 تفسیر می‌شود.
  • پورت بدون آدرس IP، مانند listen 8080; به‌عنوان 0.0.0.0:8080 رفتار می‌کند.
  • مسیر سوکت یونیکس هم می‌تواند برای ارتباط بین سرویس‌ها استفاده شود.

بعد از نرمال‌سازی، Nginx همه بلاک‌های serverی که با IP و پورت ورودی مطابقت دارند را جمع‌آوری و این قوانین اختصاصی را اعمال می‌کند:

  • بلاک‌هایی با آدرس IP خاص، بر بلاک‌هایی که از وایلدکارت (0.0.0.0) استفاده می‌کنند اولویت دارند.
  • اگر چند بلاک به یک اندازه خاص باشند، Nginx موقتاً اولین بلاک تعریف‌شده در پیکربندی را انتخاب می‌کند تا دایرکتیو server_name این تساوی را حل کند.

مدیریت سرور پیش‌فرض

هر ترکیب IP-و-پورت باید یک سرور پیش‌فرض داشته باشد؛ که درخواست‌هایی را که با هیچ server_name تعریف‌شده‌ای مطابقت ندارند مدیریت کند. اگر هیچ default_server تعریف نشده باشد، Nginx از اولین بلاک تعریف‌شده برای آن ترکیب به‌عنوان پیش‌فرض استفاده می‌کند. می‌توانید یکی را صریحاً تعریف کنید:

server {
    listen 80 default_server;
    server_name _;
}

این تضمین می‌کند درخواست‌های بدون تطابق به‌شکل قابل پیش‌بینی مدیریت شوند.

مهم است درک کنید که Nginx فقط وقتی دایرکتیو server_name را ارزیابی می‌کند که بیش از یک بلاک server با تعریف listen به یک اندازه خاص وجود داشته باشد. مثلاً:

server {
    listen 192.168.1.10;
}

server {
    listen 80;
    server_name example.com;
}

اگر درخواستی برای example.com به پورت ۸۰ روی 192.168.1.10 ارسال شود، Nginx همیشه اولین بلاک را انتخاب می‌کند؛ چون با آدرس IP دقیقاً مطابقت دارد. دایرکتیو server_name اینجا در نظر گرفته نمی‌شود.

تطبیق دایرکتیو server_name

اگر چند بلاک server همان IP و پورت را به اشتراک بگذارند، Nginx هدر Host درخواست را با دایرکتیو server_name هر بلاک مقایسه می‌کند تا مناسب‌ترین تطابق را پیدا کند. دایرکتیو server_name می‌تواند شامل نام‌های دقیق، وایلدکارت یا عبارات باقاعده باشد.

Nginx دایرکتیوهای server_name را به این ترتیب ارزیابی می‌کند:

  • تطابق دقیق: server_name دقیقاً با هدر Host مطابقت دارد.
  • وایلدکارت ابتدایی: الگویی مانند *.example.com با www.example.com یا mail.example.com مطابقت می‌یابد. اگر چند وایلدکارت ابتدایی مطابقت یابند، بلندترین انتخاب می‌شود.
  • وایلدکارت انتهایی: الگویی مانند mail.* با mail.example.com یا mail.org مطابقت می‌یابد. باز هم، بلندترین تطابق برنده است.
  • تطابق عبارت باقاعده: server_nameای که با ~ شروع شود به‌عنوان عبارت باقاعده رفتار می‌کند و اولین بلاک مطابقت‌یابنده استفاده می‌شود.
  • سرور پیش‌فرض: اگر هیچ تطابقی پیدا نشود، Nginx از بلاک سرور پیش‌فرض برای ترکیب IP و پورت فعلی استفاده می‌کند.

مثال‌ها

اگر server_name دقیقاً مطابقت یابد، آن بلاک فوراً انتخاب می‌شود:

server {
    listen 80;
    server_name *.example.com;
}

server {
    listen 80;
    server_name host1.example.com;
}

درخواستی برای host1.example.com توسط بلاک دوم مدیریت می‌شود؛ چون تطابق دقیق ارائه می‌دهد.

اگر تطابق دقیقی پیدا نشود، Nginx وایلدکارت‌های ابتدایی را بررسی می‌کند. مثلاً:

server {
    listen 80;
    server_name www.example.*;
}

server {
    listen 80;
    server_name *.example.org;
}

server {
    listen 80;
    server_name *.org;
}

درخواستی برای www.example.org توسط بلاک دوم مدیریت می‌شود؛ چون بلندترین تطابق وایلدکارت ابتدایی را ارائه می‌دهد.

بعد، Nginx وایلدکارت‌های انتهایی را بررسی می‌کند:

server {
    listen 80;
    server_name host1.example.com;
}

server {
    listen 80;
    server_name example.com;
}

server {
    listen 80;
    server_name www.example.*;
}

برای درخواستی به www.example.com، بلاک سوم استفاده می‌شود؛ چون بلندترین تطابق وایلدکارت انتهایی را ارائه می‌دهد.

اگر هیچ تطابق وایلدکارت‌ای پیدا نشود، Nginx تعاریف server_name عبارت باقاعده را به ترتیب ظاهرشان ارزیابی می‌کند. اولین مورد مطابقت‌یابنده انتخاب می‌شود. مثلاً:

server {
    listen 80;
    server_name example.com;
}

server {
    listen 80;
    server_name ~^(www|host1).*\.example\.com$;
}

server {
    listen 80;
    server_name ~^(subdomain|set|www|host1).*\.example\.com$;
}

درخواستی برای www.example.com با بلاک دوم مطابقت می‌یابد؛ چون اولین عبارت باقاعده‌ای است که جور درمی‌آید.

اگر هیچ تطابقی پیدا نشود، Nginx به اولین بلاک برای آن IP و پورت یا هر بلاکی که صریحاً از default_server استفاده می‌کند، پیش‌فرض می‌شود.

تفاوت‌های نسخه

فرایند انتخاب سرور در NGINX Open Source و NGINX Plus یکسان کار می‌کند. الگوریتم هسته، ترتیب ارزیابی و رفتار تطبیق تفاوتی ندارند. اما NGINX Plus شامل ابزارهای تشخیصی مانند ماژول‌های API و داشبورد است که بررسی پیکربندی‌های فعال و درک اینکه کدام بلاک server درخواستی را سرو می‌کند را ساده‌تر می‌کنند. هر دو نسخه از همان منطق انتخاب پیروی می‌کنند.

درک انواع بلاک Location

بعد از اینکه Nginx بلاک server مناسب را انتخاب کرد، باید تعیین کند کدام بلاک location درون آن سرور درخواست را مدیریت کند. بلاک location تعیین می‌کند Nginx چگونه URIهای خاص را پردازش کند. درک انواع مختلف تطابق‌های location برای نوشتن پیکربندی‌های قابل پیش‌بینی و قابل نگهداری ضروری است.

سینتکس بلاک Location

بلاک location با استفاده از دایرکتیو location داخل بلاک server تعریف می‌شود. شکل کلی:

location [modifier] match_pattern {
    # دایرکتیوهای پیکربندی
}
  • modifier: نمادی اختیاری که نحوه تفسیر match_pattern توسط Nginx را تغییر می‌دهد.
  • match_pattern: رشته، پیشوند یا عبارت باقاعده‌ای که برای تست در برابر URI درخواست استفاده می‌شود.

هر مادیفایر معنای خاصی دارد که بر نحوه انجام تطبیق توسط Nginx و نحوه اولویت‌بندی یک بلاک location نسبت به بلاک دیگر اثر می‌گذارد.

تطبیق پیشوندی (پیش‌فرض)

اگر هیچ مادیفایری مشخص نشده باشد، Nginx تطبیق پیشوندی (Prefix Match) انجام می‌دهد. یعنی بلاک location با همه URIهایی که با پیشوند مشخص‌شده شروع می‌شوند مطابقت می‌یابد. تطبیق‌های پیشوندی به بزرگی و کوچکی حروف حساس‌اند و رایج‌ترین نوع در پیکربندی‌های Nginx هستند.

location /images/ {
    root /var/www/static;
}

در این مثال، هر درخواستی که با /images/ شروع شود (مثل /images/logo.png یا /images/icons/set1/) توسط این بلاک پردازش می‌شود.

تطبیق‌های پیشوندی کارآمد و سریع‌اند؛ که آن‌ها را برای محتوای استاتیک و مسیریابی مبتنی بر دایرکتوری ایده‌آل می‌کند.

تطبیق دقیق (=)

اگر location با مادیفایر = تعریف شده باشد، Nginx آن را به‌عنوان تطابق دقیق (Exact Match) در نظر می‌گیرد. بلاک فقط وقتی استفاده می‌شود که URI درخواست دقیقاً با مسیر مشخص‌شده برابر باشد.

location = /index.html {
    root /var/www/site;
}

درخواستی برای /index.html دقیقاً با این بلاک مطابقت می‌یابد؛ اما /index.html?ref=home یا /index.html/ نه. تطابق‌های دقیق بالاترین اولویت را دارند و وقتی پیدا شوند، فرایند جستجو را بلافاصله پایان می‌دهند.

مدیران اغلب از تطابق‌های دقیق برای سرو کردن فایل‌های کوچک و ثابتی مانند صفحه اصلی یا favicon استفاده می‌کنند؛ جایی که مسیر تغییر نمی‌کند.

عبارت باقاعده حساس به حروف (~)

locationای که با مادیفایر ~ تعریف شده باشد، به‌عنوان عبارت باقاعده حساس به بزرگی و کوچکی حروف ارزیابی می‌شود. Nginx URI درخواست را در برابر الگوی regex تست و اولین location مطابقت‌یابنده را استفاده می‌کند.

location ~ \.(jpe?g|png|gif|ico)$ {
    root /var/www/images;
}

این مثال درخواست‌های تصویری منتهی به .jpg، .jpeg، .png، .gif یا .ico را مدیریت می‌کند. locationهای عبارت باقاعده قدرتمندند اما باید با احتیاط استفاده شوند؛ چون از تطابق‌های پیشوندی کندترند و می‌توانند پیچیدگی در دیباگ ایجاد کنند.

عبارت باقاعده بدون حساسیت به حروف (~*)

locationای که با مادیفایر ~* تعریف شده باشد، تطبیق عبارت باقاعده بدون حساسیت به بزرگی و کوچکی حروف (Case-Insensitive) انجام می‌دهد.

location ~* \.(jpe?g|png|gif|ico)$ {
    root /var/www/images;
}

این نسخه هم با /tortoise.jpg و هم با /FLOWER.PNG مطابقت می‌یابد؛ که وقتی نمی‌توان از بزرگی و کوچکی حروف درخواست‌های ورودی مطمئن بود (مثلاً با URLهای کاربر-ساخته یا قدیمی) مناسب است.

مادیفایر ^~

مادیفایر ^~ برای نشان دادن این است که اگر تطبیق پیشوندی با این بلاک بلندترین تطابق تا این لحظه باشد، Nginx باید همه بررسی‌های عبارت باقاعده بعدی را رد کند و مستقیماً از این بلاک استفاده کند.

location ^~ /static/ {
    root /var/www/assets;
}

در این حالت، اگر URI درخواست با /static/ شروع شود، Nginx بلافاصله از این بلاک استفاده می‌کند و هیچ location مبتنی بر regex را بررسی نمی‌کند. این وقتی مفید است که می‌خواهید تطبیق‌های پیشوندی بر ارزیابی‌های انعطاف‌پذیرتر اما کندترِ regex اولویت داشته باشند.

جدول خلاصه همه مادیفایرهای Location

مادیفایرنوع تطبیقحساسیت به حروفترتیب ارزیابیمثالتوضیح
(هیچ)پیشوندیحساسبلندترین پیشوندlocation /app/ {}با همه URIهای شروع‌شونده با /app/ مطابقت می‌یابد.
=دقیقحساسبالاترین اولویتlocation = /index.html {}فقط با /index.html مطابقت می‌یابد.
^~پیشوندیحساسقبل از فاز regexlocation ^~ /static/ {}بلندترین تطابق پیشوندی؛ ارزیابی regex را رد می‌کند.
~عبارت باقاعدهحساسترتیبی (اولین تطابق)location ~ \.php$ {}با الگوی regex تطبیق می‌یابد.
~*عبارت باقاعدهبدون حساسیتترتیبی (اولین تطابق)location ~* \.(jpg|png)$ {}regex را بدون توجه به حروف تطبیق می‌دهد.

مثال‌هایی از هر نوع Location

مثال ۱: تطبیق پیشوندی

location /downloads/ {
    root /var/www/public;
}

این بلاک با هر درخواستی که URIاش با /downloads/ شروع شود مطابقت می‌یابد. مثلاً /downloads/file.zip، /downloads/manual.pdf یا /downloads/archive/data.tar.gz همه اینجا مدیریت می‌شوند.

مثال ۲: تطابق دقیق

location = /robots.txt {
    root /var/www/site;
}

این بلاک فقط درخواست‌هایی را پردازش می‌کند که دقیقاً با /robots.txt مطابقت دارند. با /robots.txt/، /robots.txt?v=1 یا هر مسیر فرعی مطابقت نمی‌یابد.

مثال ۳: تطبیق عبارت باقاعده

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

این پیکربندی با همه URIهای منتهی به .php مطابقت می‌یابد؛ مانند /index.php، /user/profile.php یا /admin/login.php. چون مادیفایر ~ عبارت باقاعده حساس به بزرگی و کوچکی حروف مشخص می‌کند، /INDEX.PHP با این قانون تطابق نمی‌یابد.

مثال ۴: عبارت باقاعده بدون حساسیت به حروف

location ~* \.(jpg|jpeg|png)$ {
    root /var/www/images;
}

این بلاک با درخواست‌های فایل تصویری فارغ از بزرگی و کوچکی حروف مطابقت می‌یابد؛ مانند /photo.jpg، /photo.JPG یا /gallery/PICTURE.PNG. مادیفایر ~* الگو را بدون حساسیت به حروف می‌کند و مدیریت سازگار درخواست‌ها از کلاینت‌هایی که ممکن است در بزرگی حروف متفاوت باشند را تضمین می‌کند.

مثال ۵: مادیفایر ^~

location ^~ /api/ {
    proxy_pass http://backend_server;
}

این قانون به Nginx می‌گوید اگر URI با /api/ شروع شود، بلافاصله این بلاک را انتخاب کند؛ بدون بررسی هر location مبتنی بر regex بعدی. مثلاً درخواست‌هایی مثل /api/v1/users، /api/status یا /api/data.json همه اینجا مدیریت می‌شوند.

مادیفایرهای مختلف location به شما اجازه می‌دهند نحوه تطبیق URIهای درخواست توسط Nginx را دقیق تنظیم کنید. تطبیق‌های پیشوندی و دقیق معمولاً سریع‌تر و راحت‌تر برای استدلال‌اند؛ در حالی که تطابق‌های مبتنی بر regex انعطاف‌پذیری برای الگوهای پیچیده فراهم می‌کنند. بخش بعدی توضیح می‌دهد Nginx چگونه از این مادیفایرها استفاده می‌کند تا تعیین کند وقتی چند تطابق ممکن است، کدام بلاک location انتخاب می‌شود.

Nginx چگونه بلاک Location را انتخاب می‌کند

بعد از انتخاب بلاک server درست، Nginx تعیین می‌کند کدام بلاک location درون آن سرور، درخواست را پردازش کند. بلاک location تعیین می‌کند سرور چگونه URIهای خاص را مدیریت کند؛ چه سرو کردن فایل‌های استاتیک، چه پروکسی کردن درخواست‌ها یا بازنویسی مسیرها.

الگوریتم انتخاب از یک ترتیب سخت‌گیرانه و چندمرحله‌ای پیروی می‌کند که برای اولویت‌دادن به دقت در عین حفظ کارایی طراحی شده. هر مرحله تطابق‌های ممکن را فیلتر می‌کند تا Nginx یک location منفرد و قطعی برای URI ورودی شناسایی کند.

فاز تطبیق پیشوندی (دقیق ← بلندترین پیشوند)

اولین مرحله از انتخاب location روی تطبیق‌های پیشوندی تمرکز دارد. locationهای پیشوندی ساده‌ترین و سریع‌ترین نوع هستند؛ چون Nginx ابتدای URI را به‌عنوان رشته ساده مقایسه می‌کند. فرایند با تطابق‌های دقیق شروع و سپس به مقایسه‌های پیشوندی عمومی می‌رود.

تطابق دقیق با مادیفایر = تعریف می‌شود. وقتی URI دقیقاً با رشته مشخص‌شده برابر باشد، Nginx بلافاصله جستجو را متوقف و آن بلاک را سرو می‌کند. مثلاً:

location = /index.html {
    root /var/www/html;
}

این بلاک فقط /index.html را مدیریت می‌کند. درخواست‌هایی مثل /index.html?v=2 یا /index.html/ مطابقت نمی‌یابند. چون تطابق‌های دقیق اول ارزیابی می‌شوند، بر همه locationهای دیگر اولویت دارند.

اگر تطابق دقیقی وجود نداشته باشد، Nginx URI را در برابر همه locationهای پیشوندی دیگر (آن‌هایی بدون مادیفایر) مقایسه می‌کند. هر locationی که با شروع URI مطابقت دارد را شناسایی و بلندترین را به‌عنوان پیشوندِ کاندیدا ذخیره می‌کند.

location / {
    root /var/www/base;
}

location /images/ {
    root /var/www/media;
}

location /images/icons/ {
    root /var/www/icons;
}

برای درخواستی به /images/icons/logo.png، Nginx می‌فهمد هر سه پیشوند مطابقت دارند؛ اما /images/icons/ بلندترین است و بنابراین برنده می‌شود. ترتیبی که این locationها در پیکربندی تعریف شده‌اند مهم نیست؛ فقط طول پیشوند مطابقت‌یابنده اولویت را تعیین می‌کند.

هنگام نوشتن تطبیق‌های پیشوندی، به دام‌های رایج توجه کنید:

  • اسلش‌های انتهایی مهم‌اند. بدون اسلش انتهایی (/app در مقابل /app/)، Nginx ممکن است با /application یا /app123 هم مطابقت یابد.
  • مقایسه‌های پیشوندی به بزرگی و کوچکی حروف حساس‌اند. /Images/ و /images/ متفاوت رفتار می‌کنند.
  • پیشوند root / با همه URIها مطابقت می‌یابد اما به‌عنوان جایگزین برای درخواست‌های بدون تطابق عمل می‌کند.

تطبیق‌های پیشوندی کارآمدند؛ چون به پارس الگو نیاز ندارند. برای محتوای استاتیک، دایرکتوری‌ها یا مسیرهای URI خوش‌تعریف ایده‌آل‌اند.

قانون میان‌بر ^~

مادیفایر ^~ نحوه مدیریت تطبیق‌های پیشوندی توسط Nginx را با دستور به آن که همه ارزیابی‌های عبارت باقاعده بعدی را رد کند (اگر پیشوند مطابقت یابد)، تغییر می‌دهد. این ^~ را وقتی مفید می‌کند که می‌خواهید دایرکتوری‌ها یا مسیرهای خاصی بر قوانین regex اولویت داشته باشند.

location ^~ /static/ {
    root /var/www/assets;
}

اگر URI درخواست با /static/ شروع شود، Nginx این بلاک را انتخاب و هیچ location از نوع regex تعریف‌شده بعدی را ارزیابی نمی‌کند. این قانون به‌ویژه برای سرو کردن فایل‌های استاتیک—مانند تصاویر، CSS یا JavaScript—که باید از بررسی‌های کندتر regex عبور نکنند، مفید است.

چه زمانی از ^~ استفاده کنیم:

  • برای تضمین اینکه دایرکتوری‌های خاصی توسط بلاک‌های سرو-استاتیک مدیریت شوند.
  • برای بهبود کارایی در مسیرهای پرترافیک.
  • برای جلوگیری از اینکه قوانین پیچیده regex درخواست‌هایی که برای مسیرهای استاتیک در نظر گرفته شده‌اند را ربایند.

^~ فقط به تطبیق‌های پیشوندی اعمال می‌شود. بلاک‌های regex و تطبیق-دقیق این مادیفایر را نادیده می‌گیرند.

فاز تطبیق عبارت باقاعده

اگر هیچ پیشوند ^~ اعمال نشود، Nginx locationهای regex را بعدی ارزیابی می‌کند. این‌ها با مادیفایرهای ~ (حساس به حروف) یا ~* (بدون حساسیت به حروف) تعریف می‌شوند. locationهای عبارت باقاعده به شما انعطاف می‌دهند الگوهایی را تطبیق دهید که پیشوندها نمی‌توانند پوشش دهند؛ مانند پسوندهای فایل یا تنوع‌های بدون حساسیت به حروف.

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

location ~* \.(jpg|jpeg|png|gif|ico)$ {
    root /var/www/images;
}

در مثال بالا، بلاک اول فایل‌های .php را دقیقاً همان‌طور که نوشته شده تطبیق می‌دهد (حساس به حروف)؛ در حالی که بلاک دوم پسوندهای تصویری را فارغ از حروف تطبیق می‌دهد.

Nginx locationهای regex را به ترتیب ظاهرشان در فایل پیکربندی بررسی و در اولین تطابق موفق متوقف می‌شود. این رفتار حساس به ترتیب، منبع رایجی از سردرگمی است؛ چون تعاریف regex بعدی وقتی یک تطابق پیدا شد نادیده گرفته می‌شوند.

locationهای regex کندتر ارزیابی می‌شوند؛ چون شامل تطبیق الگو هستند؛ پس باید به‌ندرت و فقط وقتی استفاده شوند که پیشوندها نتوانند همان نتیجه را به دست آورند. در ترکیب با ^~، تطبیق‌های پیشوندی می‌توانند به کمینه‌سازی ارزیابی‌های غیرضروری regex کمک کنند.

قوانین جایگزین نهایی

اگر هیچ‌کدام از locationهای regex مطابقت نیابند، Nginx به تطابق بلندترین پیشوندِ ذخیره‌شده قبلی برمی‌گردد. این تضمین می‌کند هر درخواست یک هندلر روشن و قطعی دارد.

ترتیب کامل ارزیابی به این شکل است:

  • تطابق دقیق (=): اگر پیدا شد، بلافاصله توقف.
  • تطابق بلندترین پیشوند (کاندیدا).
  • اگر کاندیدا از ^~ استفاده کند، همین‌جا توقف.
  • ارزیابی locationهای regex به ترتیب پیکربندی.
  • اگر هیچ regex مطابقت نیافت، از بلندترین پیشوند ذخیره‌شده استفاده کن.

این ترتیب در همه نسخه‌های Nginx ثابت و سازگار است. با پیروی از این توالی، می‌توانید دقیقاً پیش‌بینی کنید کدام بلاک هر درخواست داده‌شده را پردازش می‌کند.

ری‌دایرکت‌های داخلی که ارزیابی مجدد را فعال می‌کنند

وقتی Nginx یک location را انتخاب کرد، معمولاً درخواست را کاملاً درون همان بلاک پردازش می‌کند. اما برخی دایرکتیوها URI یا جریان کنترل را به شکلی تغییر می‌دهند که باعث می‌شود Nginx فرایند انتخاب را دوباره شروع کند. این‌ها ری‌دایرکت‌های داخلی (Internal Redirect) نامیده می‌شوند و کاملاً درون Nginx رخ می‌دهند. کلاینت هرگز آن‌ها را نمی‌بیند؛ اما از دید Nginx، آن‌ها مثل درخواست‌های جدیدی با URI متفاوت رفتار می‌شوند.

یکی از رایج‌ترین فعال‌سازها، دایرکتیو index است. وقتی مسیر دایرکتوری‌ای مثل /blog/ درخواست می‌شود، Nginx به دنبال فایل‌های ایندکس پیش‌فرضی مانند index.html یا index.php می‌گردد. اگر یکی پیدا کند، داخلی به آن فایل ری‌دایرکت می‌کند و جستجوی location را برای /blog/index.html ری‌استارت می‌کند. مرورگر همچنان /blog/ را نمایش می‌دهد؛ اما Nginx در پس‌زمینه فایل ایندکس را سرو می‌کند.

دایرکتیو try_files هم ری‌دایرکت‌های داخلی تولید می‌کند. چندین مسیر ممکن را تست و اولین مورد موجود را سرو می‌کند. مثلاً:

location / {
    root /var/www/html;
    try_files $uri $uri/ /index.html;
}

وقتی کاربری /about را درخواست می‌کند، Nginx /var/www/html/about را بررسی می‌کند، بعد /var/www/html/about/ و اگر هیچ‌کدام وجود نداشته باشند، داخلی به /index.html ری‌دایرکت می‌کند. URI دوباره ارزیابی می‌شود و هر قانونی که با /index.html مطابقت یابد حالا اعمال می‌شود. این رفتار به‌طور گسترده در اپلیکیشن‌های تک‌صفحه‌ای (SPA) استفاده می‌شود؛ جایی که همه مسیرهای بدون تطابق به یک صفحه ورودی مشترک اشاره می‌کنند.

دایرکتیو rewrite هم منبع دیگری از ری‌دایرکت‌های داخلی است. وقتی با فلگ last (یا بدون فلگ) استفاده شود، URI را بازنویسی و فرایند انتخاب را ری‌استارت می‌کند.

location /old/ {
    rewrite ^/old/(.*)$ /new/$1 last;
}

درخواستی به /old/page.html به /new/page.html بازنویسی می‌شود و Nginx سپس دوباره برای location مناسب جستجو می‌کند. اگر به‌جای last از break استفاده شده بود، بازنویسی URI را تغییر می‌داد اما در همان location می‌ماند. فلگ‌های redirect و permanent در مقابل، ری‌دایرکت‌های خارجی به کلاینت می‌فرستند به‌جای انجام داخلی.

در نهایت، دایرکتیو error_page از ری‌دایرکت‌های داخلی برای مدیریت خطاها استفاده می‌کند. اگر فایلی مفقود یا ممنوع باشد، Nginx می‌تواند به‌جای پیام خطای پیش‌فرض، صفحه سفارشی سرو کند:

error_page 404 /custom_404.html;

location /custom_404.html {
    root /var/www/errors;
}

وقتی 404 رخ می‌دهد، Nginx داخلی به /custom_404.html ری‌دایرکت، URI را دوباره ارزیابی و آن صفحه را با وضعیت 404 سرو می‌کند. پیکربندی‌های مشابه می‌توانند خرابی‌های upstream مانند خطاهای 502 یا 504 را مدیریت کنند.

ری‌دایرکت‌های داخلی می‌توانند خودکار هم رخ دهند. اگر دایرکتوری بدون اسلش انتهایی درخواست شود، Nginx اسلش را اضافه و URI را دوباره پردازش می‌کند. ماژول‌هایی مانند auth_request یا mirror می‌توانند زیردرخواست‌هایی ایجاد کنند که مستقلاً از همان منطق انتخاب پیروی می‌کنند.

در طول ری‌دایرکت داخلی، Nginx زمینه location فعلی را دور می‌ریزد و ارزیابی را با URI جدید—این بار از فاز تطبیق-دقیق شروع—ری‌استارت می‌کند. برای جلوگیری از حلقه‌های بی‌نهایت، Nginx ری‌دایرکت‌های داخلی را با دایرکتیو max_internal_redirects محدود می‌کند که پیش‌فرضش ۱۰ است. اگر از این حد عبور کند، Nginx به‌جای ادامه، خطا برمی‌گرداند.

دیباگ ری‌دایرکت‌های داخلی اغلب نیازمند فعال کردن لاگینگ تفصیلی است:

error_log /var/log/nginx/error.log debug;

لاگ‌ها پیام‌هایی مانند rewrite or internal redirection to "/new/path" نشان می‌دهند؛ که مشخص می‌کند ری‌دایرکتی رخ داده. همچنین می‌توانید $request_uri (مسیر اصلی) و $uri (URI مؤثر) را لاگ کنید تا ببینید Nginx چگونه درخواست‌ها را حین پردازش تغییر می‌دهد.

ری‌دایرکت‌های داخلی به Nginx انعطاف فوق‌العاده‌ای می‌دهند. آن‌ها URLهای تمیز، مدیریت متمرکز خطا و بازنویسی‌های یکپارچه را—همگی بدون درخواست‌های اضافی کلاینت—فراهم می‌کنند. اما چون ارزیابی location را ری‌استارت می‌کنند، باید باتدبیر استفاده شوند تا از حلقه‌ها یا پیچیدگی غیرضروری اجتناب شود.

دام‌های رایج پیکربندی

حتی کاربران باتجربه Nginx گاهی با مشکلات پیکربندی‌ای روبه‌رو می‌شوند که به مسیریابی غیرمنتظره، سرو کردن نادرست فایل یا حلقه‌های بازنویسی منجر می‌شود. بیشتر این مشکلات از سوءتفاهم‌هایی درباره نحوه تفسیر بلاک‌های location توسط Nginx و ترتیبی که آن‌ها را ارزیابی می‌کند ناشی می‌شوند. شناخت این دام‌ها می‌تواند زمان دیباگ قابل توجهی ذخیره کند و تضمین کند پیکربندی‌تان طبق انتظار رفتار می‌کند.

اشتباهات ترتیب‌بندی Locationهای Regex

یکی از رایج‌ترین مشکلات وقتی رخ می‌دهد که چند location عبارت باقاعده در یک بلاک server منفرد تعریف شده باشند. Nginx locationهای regex را به ترتیب ظاهرشان ارزیابی و در اولین تطابق متوقف می‌شود. این می‌تواند به موقعیت‌هایی منجر شود که الگوی عمومی، ناخواسته الگوی خاص‌تر را بازنویسی کند.

مثلاً:

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

location ~ /api/.*\.php$ {
    fastcgi_pass unix:/run/php/api.sock;
}

در نگاه اول ممکن است به نظر برسد درخواست‌های API مثل /api/data.php به بلاک دوم بروند؛ اما نمی‌روند. regex اول یعنی \.php$ اول مطابقت می‌یابد؛ پس همه درخواست‌های .php، شامل endpointهای API، توسط قانون عمومی مدیریت می‌شوند.

رویکرد درست، قرار دادن locationهای regex خاص‌تر قبل از عمومی‌ترهاست:

location ~ /api/.*\.php$ {
    fastcgi_pass unix:/run/php/api.sock;
}

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

نگه‌داشتن locationهای regex پیچیده در انتهای فایل و ترتیب‌بندی عامدانه‌شان، یک بهترین روش برای اجتناب از این سردرگمی است.

سوءبرداشت مادیفایر ^~

اشتباه مکرر دیگری مربوط به مادیفایر ^~ است. برخی کاربران فرض می‌کنند ^~ اولویت یک location را نسبت به تطابق‌های regex افزایش می‌دهد؛ حتی وقتی URI با پیشوند تعریف‌شده شروع نمی‌شود. در واقعیت، ^~ فقط وقتی اعمال می‌شود که URI درخواست با پیشوند شروع شود. اگر نشود، قانون نادیده گرفته و جستجو مثل معمول ادامه می‌یابد.

مثلاً:

location ^~ /media/ {
    root /var/www/files;
}

location ~ \.jpg$ {
    root /var/www/images;
}

درخواستی به /images/logo.jpg از بلاک /media/ استفاده نمی‌کند؛ حتی با اینکه ^~ تعریف شده؛ چون مسیر با /media/ شروع نمی‌شود.

مادیفایر ^~ سازوکار اولویت جهانی نیست؛ فقط میان‌بری است که به Nginx می‌گوید ارزیابی regex را رد کند وقتی پیشوند خاصی مطابقت یافت. اگر نادرست استفاده شود، می‌تواند به سردرگمی یا رد شدن ناخواسته برخی قوانین regex منجر شود.

تطبیق‌های دقیق دایرکتوری در مقابل فایل

قاطی کردن locationهای سبک-دایرکتوری و سبک-فایل آسان است؛ به‌ویژه وقتی هر دو در همان پیکربندی وجود دارند. مثلاً:

location /downloads/ {
    root /var/www/files;
}

location = /downloads {
    return 301 /downloads/;
}

بلاک دوم دقیقاً با /downloads (بدون اسلش انتهایی) مطابقت می‌یابد؛ در حالی که اولی /downloads/ و همه زیرش را مدیریت می‌کند. اگر ریدایرکت در بلاک دوم نباشد، کاربرانی که به /downloads دسترسی دارند، 404 می‌بینند؛ حتی با اینکه /downloads/ معتبر است.

هنگام تعریف دایرکتوری‌ها، همیشه اسلش انتهایی را در الگوی location بگنجانید تا مطمئن شوید درخواست‌های دایرکتوری و محتوایشان درست مدیریت می‌شوند. اگر می‌خواهید هر دو حالت را تمیز مدیریت کنید، می‌توانید از یک ریدایرکت کوتاه استفاده کنید:

location = /downloads {
    return 301 /downloads/;
}

رفتار ناخواسته try_files

دایرکتیو try_files می‌تواند مسیریابی غیرمنتظره ایجاد کند؛ اگر ترتیب آرگومان‌ها نادرست باشد یا اگر مسیر جایگزین نهایی به URIای اشاره کند که ری‌دایرکت داخلی دیگری فعال می‌کند.

مثلاً:

location / {
    try_files $uri /index.html;
}

این پیکربندی برای محتوای استاتیک کار می‌کند اما با اپلیکیشن‌های داینامیک می‌تواند بدرفتاری کند. اگر /index.html وجود نداشته باشد، Nginx به‌جای ارسال درخواست به upstream، 404 برمی‌گرداند. الگوی امن‌تر شامل جایگزینی به یک location نام‌دار یا مسیر پروکسی است:

location / {
    try_files $uri /index.html @app;
}

location @app {
    proxy_pass http://backend;
}

استفاده از try_files با منطق جایگزینی شفاف تضمین می‌کند فایل‌های مفقود یا URIهای بازنویسی‌شده همیشه با ظرافت مدیریت شوند.

وایلدکارت‌ها در server_name

سوءاستفاده از وایلدکارت‌ها در تعاریف server_name می‌تواند باعث مسیریابی درخواست‌ها به سرور مجازی اشتباه شود. پیکربندی‌ای مثل:

server_name *.example.com;

با www.example.com، api.example.com و هر زیردامنه دیگری مطابقت می‌یابد. اما اگر بلاک server دیگری server_name example.com; را تعریف کرده باشد، درخواست‌هایی برای دامنه اصلی (example.com) با بلاک وایلدکارت مطابقت نمی‌یابند و به‌جای آن به سرور پیش‌فرض برمی‌گردند.

برای مدیریت سازگار هم دامنه ریشه و هم زیردامنه‌هایش، هر دو نام را صریحاً تعریف کنید:

server_name example.com *.example.com;

این کار از ابهام اجتناب و انتخاب قابل پیش‌بینی هاست مجازی را تضمین می‌کند.

بلاک‌های Server مبهم

ابهام اغلب وقتی ایجاد می‌شود که چند بلاک server همان دایرکتیو listen را به اشتراک بگذارند اما مقادیر server_name مشابهی داشته باشند. در چنین مواردی، Nginx آن‌که خاص‌ترین تطابق با هدر Host را دارد انتخاب می‌کند؛ یا اگر هیچ تطابق صریحی وجود نداشته باشد، آن‌که به‌عنوان default_server مشخص شده.

این پیکربندی را در نظر بگیرید:

server {
    listen 80 default_server;
    server_name _;
    return 444;
}

server {
    listen 80;
    server_name example.com;
    root /var/www/example;
}

اگر درخواست از هدر Hostی استفاده کند که با example.com مطابقت ندارد، Nginx آن را از سرور پیش‌فرض سرو و اتصال را فوراً با 444 می‌بندد. هرچند این می‌تواند برای امنیت عامدانه باشد، مهم است که سرور پیش‌فرض را صریحاً تعریف کنید تا فرض نکنید Nginx خودش یکی را انتخاب می‌کند.

ترکیب Regex و Prefix بدون احتیاط

ترکیب locationهای regex و پیشوندی می‌تواند مسائل ظریفی ایجاد کند اگر رفتارشان به‌وضوح درک نشده باشد. پیشوندها اول ارزیابی می‌شوند و فقط اگر بهترین پیشوند از ^~ استفاده نکند، ارزیابی regex رخ می‌دهد. مثلاً:

location /api/ {
    proxy_pass http://backend;
}

location ~ /api/v[0-9]+/ {
    proxy_pass http://versioned_api;
}

در این راه‌اندازی، /api/v1/users با هر دو location مطابقت دارد. چون /api/ پیشوند است و از ^~ استفاده نمی‌کند، Nginx به بررسی locationهای regex ادامه می‌دهد. چون regex مطابقت می‌یابد، انتخاب می‌شود. اگر می‌خواهید /api/ همه مسیرهای API را فارغ از قوانین regex مدیریت کند، افزودن ^~ آن رفتار را تضمین می‌کند:

location ^~ /api/ {
    proxy_pass http://backend;
}

بهترین روش‌ها

درک نحوه انتخاب بلاک‌های server و location توسط Nginx فقط قدم اول است. اعمال مؤثر آن دانش تضمین می‌کند پیکربندی‌ها قابل پیش‌بینی، کارآمد و نگهداری‌آسان باقی بمانند. با گذشت زمان و رشد پیکربندی‌تان، ناسازگاری‌های کوچک یا دایرکتیوهای بدجای‌گذاری‌شده می‌توانند به مسائل مسیریابی ظریف منجر شوند. پیروی از مجموعه‌ای از روش‌های ثابت به شما کمک می‌کند از این مشکلات اجتناب و راه‌اندازی‌تان را تمیز و مقیاس‌پذیر نگه دارید.

روش‌های زیر از دیپلوی‌های دنیای واقعی و توصیه‌های جامعه و همچنین راهنمایی مستندات رسمی Nginx گرفته شده‌اند.

از تطبیق‌های دقیق به‌ندرت اما عامدانه استفاده کنید

locationهای تطبیق-دقیق خاص‌ترین شکل مدیریت URI هستند. برای مسیرهای کوچک و ثابتی که نباید با بقیه هم‌پوشانی داشته باشند مفیدند؛ مانند هلت‌چک‌ها، فایل‌های استاتیک یا ریدایرکت‌ها. اما باید با هدف روشن استفاده شوند و نه به‌عنوان جایگزین مسیریابی مبتنی بر پیشوند.

location = /robots.txt {
    root /var/www/site;
}

location = /healthz {
    return 200 'OK';
}

استفاده بیش‌ازحد از تطبیق دقیق برای مسیرهای معمولی می‌تواند پیکربندی‌ها را سخت‌تر نگهداری کند؛ به‌ویژه وقتی بعداً لازم باشد مسیریابی را تغییر دهید. آن‌ها را برای مواردی نگه دارید که رفتار همیشه باید ایزوله و قطعی باشد.

تطبیق‌های پیشوندی را برای مسیریابی سطح-دایرکتوری ترجیح دهید

locationهای پیشوندی سریع، کارآمد و برای سرو کردن محتوای استاتیک یا گروه‌بندی فایل‌های مرتبط زیر دایرکتوری‌های مشترک ایده‌آل‌اند. از سربار محاسباتی عبارات باقاعده اجتناب می‌کنند و چشمی بسیار راحت‌تر قابل استدلال‌اند.

مثلاً:

location /static/ {
    root /var/www/assets;
}

location /images/ {
    root /var/www/images;
}

این بلاک‌ها بلافاصله روشن می‌کنند کدام منابع به کجا تعلق دارند. هنگام کار با سلسله‌مراتب پیچیده دایرکتوری، تطبیق‌های پیشوندی تعادلی بین خوانایی و کارایی ارائه می‌دهند.

از عبارات باقاعده غیرضروری اجتناب کنید

locationهای عبارت باقاعده قدرتمندند؛ اما سربار کارایی ایجاد می‌کنند و می‌توانند رفتار را سخت‌تر قابل پیش‌بینی کنند. فقط وقتی باید استفاده شوند که تطبیق پیشوندی نتواند همان هدف را محقق کند؛ مثلاً هنگام تطبیق پسوندهای فایل یا الگوهای داینامیک.

پیکربندی‌ای که برای هر مسیر از regex استفاده می‌کند نه‌فقط ناکارآمد بلکه مستعد خطای بیشتری هم هست. به‌جای:

location ~* /static/.*\.(css|js|png|jpg)$ {
    root /var/www/assets;
}

می‌توانید همان نتیجه را با ترکیبی از پیشوندها و مدیریت MIME-Type به دست آورید:

location /static/ {
    root /var/www/assets;
}

این رویکرد تمیزتر، سریع‌تر و راحت‌تر برای دیباگ است. از regex به‌ندرت استفاده کنید و وقتی استفاده می‌کنید، آن‌ها را نزدیک انتهای فایل قرار دهید تا از تطابق‌های ناخواسته جلوگیری شود.

locationهای Regex را در انتهای پیکربندی قرار دهید

چون Nginx locationهای regex را به ترتیب ظاهرشان ارزیابی می‌کند، همیشه آن‌ها را بعد از همه بلاک‌های پیشوندی و تطبیق-دقیق تعریف کنید. این ارزیابی قابل پیش‌بینی را تضمین و از بازنویسی‌شدن مسیرهای خاص مبتنی بر مسیر توسط regexهای عمومی جلوگیری می‌کند.

ساختار خوبی که باید دنبال شود:

  • تطابق‌های دقیق (=) در بالا.
  • تطابق‌های پیشوندی (با و بدون ^~) بعدی.
  • تطابق‌های regex (~، ~) در انتها.*

این الگوی ترتیب‌بندی ساده، پیکربندی‌تان را خوانا و بین پروژه‌ها سازگار می‌کند.

از try_files بادقت و صراحت استفاده کنید

دایرکتیو try_files قدرتمند است اما اگر بدون هدف روشن استفاده شود می‌تواند گیج‌کننده باشد. همیشه باید با یک جایگزین صریح پایان یابد (یا یک فایل استاتیک، یا یک location نام‌دار، یا یک کد خطای تعریف‌شده) تا از ابهام اجتناب شود.

location / {
    try_files $uri $uri/ =404;
}

برای اپلیکیشن‌هایی که به مسیریابی داینامیک یا جایگزینی فرانت‌اند نیاز دارند، از locationهای نام‌دار یا بلاک‌های پروکسی استفاده کنید:

location / {
    try_files $uri $uri/ @app;
}

location @app {
    proxy_pass http://backend;
}

از اشاره‌دادن جایگزین‌های try_files مستقیماً به مسیرهایی که ممکن است ری‌دایرکت‌های داخلی جدید فعال کنند اجتناب کنید؛ مگر اینکه آن رفتار عامدانه باشد.

وقتی ممکن است از return به‌جای rewrite استفاده کنید

دایرکتیو rewrite اغلب برای ریدایرکت‌های ساده‌ای بیش‌ازحد استفاده می‌شود که می‌توانند کارآمدتر با return مدیریت شوند. دایرکتیو rewrite پارس URI را فعال می‌کند و در برخی موارد ری‌دایرکت‌های داخلی؛ که برای ریدایرکت‌های استاتیک یا دائمی غیرضروری‌اند.

به‌جای:

rewrite ^/old-page$ /new-page permanent;

ترجیح بدهید:

return 301 /new-page;

دایرکتیو return خواناتر، سریع‌تر در اجرا و واضح‌تر در هدف است؛ به‌ویژه برای ریدایرکت‌های دائمی یا ساده.

ترتیب‌بندی بلاک‌های Server را قابل پیش‌بینی نگه دارید

وقتی چند بلاک server روی همان IP و پورت گوش می‌دهند، ترتیبی که در پیکربندی ظاهر می‌شوند می‌تواند تعیین کند کدام‌یک نقش پیش‌فرض را بازی کند. برای صریح کردن این موضوع، همیشه یک سرور پیش‌فرض تعیین کنید:

server {
    listen 80 default_server;
    server_name _;
    return 444;
}

این تضمین می‌کند درخواست‌هایی که با هیچ server_name تعریف‌شده‌ای مطابقت ندارند، به‌شکل کنترل‌شده مدیریت شوند. تعریف صریح یک بلاک جایگزین، هنگام دیباگِ مسیریابی غیرمنتظره هم کمک می‌کند.

پیکربندی‌ها را برای خوانایی ساختاردهی کنید

پیکربندی‌های پیچیده در طول زمان تکامل می‌یابند. برای نگهداری‌پذیر نگه‌داشتنشان:

  • دایرکتیوهای مرتبط را کنار هم گروه‌بندی کنید.
  • از تورفتگی (Indentation) و فاصله‌گذاری سازگار استفاده کنید.
  • توضیحات (Comment) برای توصیف رفتارهای نامشهود بگنجانید.
  • پیکربندی‌های بزرگ را به فایل‌های include شده جدا کنید (مثلاً include conf.d/*.conf;).

فایل‌های پیکربندی خوانا اشتباهات را کاهش می‌دهند و فهم یا عیب‌یابی راه‌اندازی برای دیگران را ساده‌تر می‌کنند.

پیکربندی‌ها را مکرراً اعتبارسنجی و تست کنید

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

nginx -t

اگر تست پاس شد، به‌شکل آرام ریلود کنید:

nginx -s reload

برای دیباگ رفتار مسیریابی، از:

curl -I -v http://example.com/path

استفاده و بررسی کنید کدام بلاک location درخواست را سرو می‌کند. فعال کردن موقت لاگ دیباگ (error_log /var/log/nginx/error.log debug;) هم می‌تواند نشان دهد Nginx چگونه از locationها عبور و ری‌دایرکت‌های داخلی را اعمال می‌کند.

پیکربندی‌ها را تا جای ممکن قطعی (Deterministic) نگه دارید

پیکربندی قطعی برای هر درخواست به یک شکل رفتار می‌کند؛ بدون غافلگیری. از الگوهای هم‌پوشان متعدد اجتناب، از مادیفایرهای شفاف استفاده و هر استثنایی را مستند کنید. وقتی شک دارید، مسیرهای مبهم را صریحاً تست کنید. قابل پیش‌بینی بودن نه‌فقط قابلیت اطمینان بلکه نگهداری بلندمدت را هم ساده‌تر می‌کند.

سوالات متداول

۱. آیا Nginx locationهای regex را قبل از تطبیق‌های پیشوندی پردازش می‌کند؟

نه. Nginx همیشه تطبیق‌های پیشوندی را اول ارزیابی می‌کند؛ شامل هر دو location دقیق (=) و پیشوندی عمومی. locationهای regex فقط بعد از اینکه Nginx بهترین تطابق پیشوندی را شناسایی کرد بررسی می‌شوند.

ترتیب ارزیابی سخت‌گیرانه است:

  • بررسی برای تطابق‌های دقیق (=).
  • شناسایی بلندترین تطابق پیشوندی.
  • اگر آن پیشوند از ^~ استفاده کند، توقف و استفاده فوری از آن.
  • در غیر این صورت، ارزیابی locationهای regex (~، ~*) به ترتیب پیکربندی.

این یعنی locationهای regex هرگز بر پیشوندهای دقیق یا ^~ غلبه نمی‌کنند و فقط بعد از ارزیابی پیشوند در نظر گرفته می‌شوند. regexها پردازش بیشتری هم لازم دارند؛ پس به‌طور عامدانه برای دلایل کارایی به تعویق افتاده‌اند.

۲. مادیفایر ^~ واقعاً چه کاری انجام می‌دهد؟

مادیفایر ^~ به Nginx می‌گوید فاز regex را رد کند اگر پیشوند مطابقی پیدا شود. اولویت یک location را به‌طور کلی بالا نمی‌برد؛ فقط بر چیزی که وقتی URI با آن پیشوند شروع می‌شود اثر می‌گذارد.

مثال:

location ^~ /assets/ {
    root /var/www/static;
}

location ~ \.(jpg|png)$ {
    root /var/www/images;
}

درخواستی به /assets/logo.png با پیشوند /assets/ مطابقت می‌یابد و چون از ^~ استفاده می‌کند، Nginx همان‌جا توقف و هیچ قانون regex را تست نمی‌کند (حتی با اینکه الگوی .png هم مطابقت می‌یافت).

می‌توانید ^~ را به‌عنوان دایرکتیوی در نظر بگیرید که می‌گوید: «اگر این پیشوند جور درآمد، زحمت بررسی locationهای regex را نکش.» اغلب برای مسیرهای استاتیکی مانند /api/، /media/ یا /css/ استفاده می‌شود تا قوانین regex دخالت نکنند.

۳. چطور دیباگ کنم کدام location را Nginx انتخاب کرده؟

برای تأیید اینکه کدام location درخواستی را مدیریت کرده، می‌توانید از چند ابزار داخلی Nginx استفاده کنید. مؤثرترین روش، فعال کردن موقت لاگ دیباگ است:

error_log /var/log/nginx/error.log debug;

سپس با curl -I -v http://example.com/path درخواستی بزنید و لاگ خطا را مرور کنید. خطوطی مانند این شامل خواهد بود:

using configuration "/etc/nginx/nginx.conf"
rewrite or internal redirection to "/index.html"
using location: "/images/"

این پیام‌ها دقیقاً نشان می‌دهند کدام location مطابقت یافته و آیا ری‌دایرکت‌های داخلی رخ داده یا نه.

همچنین می‌توانید متغیرهای $uri، $request_uri و $request_filename را در لاگ‌های دسترسی‌تان ثبت کنید تا ببینید Nginx چگونه درخواست را تغییر داده. مثلاً:

log_format debug '$remote_addr $request_uri => $uri => $request_filename';

در نهایت، می‌توانید اجرا کنید:

nginx -T

تا کل پیکربندی (شامل بلاک‌های به ارث برده) چاپ و ترتیب و مادیفایرهای همه تعاریف location تأیید شود.

این روش‌ها در کنار هم تصویر کاملی از فرایند تصمیم‌گیری Nginx به شما می‌دهند.

۴. چرا تطابق دقیق من فعال نمی‌شود؟

تطبیق‌های دقیق فقط وقتی اعمال می‌شوند که URI درخواست دقیقاً با رشته مشخص‌شده برابر باشد: بدون اسلش انتهایی، پارامترهای کوئری یا تغییرات حروف.

مثلاً:

location = /index.html {
    root /var/www/html;
}

این location با /index.html مطابقت می‌یابد اما نه /index.html/ و نه /index.html?v=1. اگر درخواست‌های شما پارامتر دارند، Nginx آن‌ها را قبل از تطبیق جدا می‌کند؛ اما هر کاراکتر مسیر اضافی یا عدم تطابق حروف همچنان باعث شکست تطبیق می‌شود.

در بسیاری از موارد، مشکل این است که درخواست به اسلش ختم می‌شود یا ری‌دایرکت داخلی فعال می‌کند (مثل ریدایرکت شدن / به /index.html). در آن موارد، URI بازنویسی‌شده ممکن است دیگر با قانون = مطابقت نداشته باشد.

برای تأیید، لاگ دیباگ را فعال و به دنبال ورودی‌هایی بگردید که نشان می‌دهند آیا URI قبل از تطبیق بازنویسی شده یا نه. اگر هدف‌تان تطبیق با چند شکل از همان مسیر است، یک location پیشوندی یا regex ممکن است مناسب‌تر باشد.

۵. آیا ترتیب بلاک‌های location مهم است؟

بسته به نوع location:

  • locationهای پیشوندی (/path/، ^~ /path/، /) بر اساس بلندترین تطابق ارزیابی می‌شوند، نه ترتیب فایل. ترتیبی که در فایل پیکربندی ظاهر می‌شوند هیچ اثری ندارد.
  • locationهای regex (~، ~*) اما به ترتیب وابسته‌اند. به ترتیب تعریف به‌صورت ترتیبی تست می‌شوند و اولین regex مطابقت‌یابنده برنده می‌شود.

مثلاً:

location ~ \.php$ { ... }
location ~ /api/.*\.php$ { ... }

اینجا، regex اول همه فایل‌های .php را می‌گیرد و جلوی مطابقت‌یافتن دومی را می‌گیرد. معکوس کردن ترتیب به الگوی خاص‌تر اولویت می‌دهد.

پس در حالی که locationهای پیشوندی از ترتیب پیکربندی تأثیر نمی‌پذیرند، locationهای regex به آن حساس‌اند؛ که جای‌گذاری مناسب را برای نتایج قابل پیش‌بینی ضروری می‌کند.

۶. چرا بلاک default_server درخواست مرا مدیریت می‌کند؟

وقتی چند بلاک server روی همان IP و پورت گوش می‌دهند و هیچ‌کدام از مقادیر server_nameشان با هدر Host ورودی مطابقت ندارد، Nginx درخواست را به سرور تعریف‌شده به‌عنوان default_server می‌فرستد.

server {
    listen 80 default_server;
    server_name _;
    return 444;
}

اگر هیچ پیش‌فرضی مشخص نشده باشد، Nginx از اولین بلاک server که با آن روبه‌رو می‌شود به‌عنوان پیش‌فرض ضمنی استفاده می‌کند. این می‌تواند وقتی درخواست‌ها به هاست مجازی غیرمنتظره‌ای مسیریابی می‌شوند، سردرگمی ایجاد کند. همیشه یک default_server صریح تعریف کنید تا رفتار قابل پیش‌بینی باشد.

۷. چرا Nginx وقتی فایل وجود دارد 404 برمی‌گرداند؟

ممکن است 404 حتی وقتی فایل فیزیکی وجود دارد ظاهر شود؛ به‌دلیل عدم تطابق بین URI و مسیر فایل‌سیستمی که توسط دایرکتیو root یا alias تولید شده.

اگر از alias استفاده می‌کنید، مطمئن شوید مسیر بخش مطابقت‌یافته URI را به‌درستی جایگزین می‌کند:

location /static/ {
    alias /var/www/assets/;
}

با alias، URI /static/logo.png به /var/www/assets/logo.png resolve می‌شود. اگر به‌اشتباه از root استفاده کنید:

location /static/ {
    root /var/www/assets;
}

مسیر resolve شده /var/www/assets/static/logo.png می‌شود؛ که ممکن است وجود نداشته باشد. تفاوت‌های کوچک مسیری مثل این اغلب 404های غیرمنتظره را توضیح می‌دهند.

نتیجه‌گیری

در این راهنما پوشش دادیم که Nginx چگونه بلاک‌های server و location درست را برای هر درخواست انتخاب می‌کند؛ شامل اینکه listen و server_name چگونه انتخاب سرور را تعیین می‌کنند و locationها به چه ترتیبی ارزیابی می‌شوند: تطابق دقیق، بلندترین پیشوند، میان‌بر اختیاری ^~، ارزیابی regex و جایگزینی پیشوند.

همچنین توضیح دادیم که دایرکتیوهایی مثل try_files، index، rewrite و error_page چگونه می‌توانند ری‌دایرکت‌های داخلی فعال کنند که فرایند ارزیابی را ری‌استارت می‌کنند و مادیفایرهایی مانند =، ^~، ~ و ~* چگونه بر رفتار تطبیق اثر می‌گذارند. در این مسیر، دام‌های رایج پیکربندی، بهترین روش‌ها برای مسیریابی قابل پیش‌بینی و تکنیک‌های عملی دیباگ را کاوش کردید.

با این اصول در ذهن، می‌توانید پیکربندی‌های Nginx بسازید که کارآمد، سازگار و نگهداری‌آسان هستند و تضمین کنید هر درخواست دقیقاً طبق انتظار مدیریت می‌شود.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا