توی این درس یاد میگیری داخل یک سایت، برای مسیرهای مختلف رفتار جدا تعریف کنی: /api/ به اپلیکیشن برود، /static/ مستقیم از دیسک با کش طولانی، عکسها با یک قانون و بقیه با قانونی دیگر. ابزارش بلوک location است. چهار نوعش را میشناسی (پیشوندی، دقیق =، ^~ و regex با ~ و ~*) و مهمتر از همه، ترتیب دقیق انتخاب بینشان را؛ که منبع بیشتر سردرگمیهاست. با try_files میگویی «اول این فایل را امتحان کن، نبود آن یکی، نبود فلان کار»، و فرق root و alias را با یک باگ امنیتی واقعی میبینی.
مسئله: یک سایت، چند جور مسیر
Section titled “مسئله: یک سایت، چند جور مسیر”سایت واقعی فقط «یک پوشه فایل» نیست. یک فروشگاه را در نظر بگیر:
/و صفحههای فروشگاه ← اپلیکیشن (یا فایلهای build شدهی یک SPA)/api/...← سرور API، با timeout و هدرهای خودش/static/...← CSS و JS، مستقیم از دیسک، با کش یکساله/uploads/...← عکسهای کاربران، از پوشهای دیگر روی دیسک، بدون اجازهی اجرای هیچ اسکریپتی/admin← فقط از IP دفتر
همه زیر یک دامنه و یک server block. Nginx باید برای هر آدرس بداند کدام قانون اعمال شود.
تشبیه: میز پذیرش و برگهی مسیریابی
Section titled “تشبیه: میز پذیرش و برگهی مسیریابی”اگر server block پذیرش یک ساختمان باشد (درس قبل)، locationها برگهی مسیریابی روی میز پذیرشاند: «هر کس دنبال اتاق ۱۰۵ است، مستقیم برود آنجا» (تطبیق دقیق =)؛ «هر کس دنبال طبقهی سوم است، آسانسور سمت راست» (پیشوندی)؛ «هر کس بستهی پستی دارد، از هر طبقهای، برود انبار» (regex روی پسوند). مشکل وقتی است که دو قانون با هم جور شوند؛ آنوقت باید بدانی پذیرش کدام را اول نگاه میکند.
چهار نوع location
Section titled “چهار نوع location”| شکل | اسم | جور میشود با | نمونه |
|---|---|---|---|
location = /path |
دقیق | فقط دقیقاً همان مسیر | location = /health |
location ^~ /path/ |
پیشوندی با اولویت | هر مسیری که با آن شروع شود، و جلوی بررسی regex را میگیرد | location ^~ /static/ |
location ~ regex |
regex (حساس به حروف) | هر مسیری که regex با آن جور شود | location ~ \.php$ |
location ~* regex |
regex (بدون حساسیت به حروف) | همان، JPG و jpg یکی |
location ~* \.(jpg|png)$ |
location /path |
پیشوندی معمولی | هر مسیری که با آن شروع شود | location /api/ |
مثالهای عملی
Section titled “مثالهای عملی”این درس روی ماشین آزمایشی server (Ubuntu 24.04، Nginx 1.24) با کاربر root اجرا شده. برای اینکه ببینیم کدام location جواب داده، اول بیشتر مثالها بهجای فایل واقعی، با return 200 "اسم location" جواب میدهند.
یک تابع کوچک برای آزمایش چند آدرس پشت هم (t کد و بدنهی پاسخ را در یک خط چاپ میکند):
cat > /usr/local/bin/t <<'EOF'#!/usr/bin/env bash# t URI...: print status code and body (first line) for each URI on shop.testfor u in "$@"; do printf '%-26s -> ' "$u" curl -s --path-as-is -o /tmp/t.body -w '%{http_code} ' "http://shop.test$u" head -n 1 /tmp/t.body | tr -d '\r' | cut -c1-60doneEOFchmod +x /usr/local/bin/techo "ready"readyمثال ۱: location پیشوندی، بلندترین برنده است
Section titled “مثال ۱: location پیشوندی، بلندترین برنده است”server { listen 80; server_name shop.test;
location / { return 200 "prefix /\n"; } location /images/ { return 200 "prefix /images/\n"; } location /images/products/ { return 200 "prefix /images/products/\n"; }}ln -sf /etc/nginx/sites-available/shop.test /etc/nginx/sites-enabled/nginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t / /about /images/logo.png /images/products/42.jpg /imagesXnginx: configuration file /etc/nginx/nginx.conf test is successful/ -> 200 prefix //about -> 200 prefix //images/logo.png -> 200 prefix /images//images/products/42.jpg -> 200 prefix /images/products//imagesX -> 200 prefix /location /با همه چیز جور میشود (هر مسیری با/شروع میشود)؛ پس «پیشفرض» سایت است./images/products/42.jpgبا هر سه پیشوند جور است؛ بلندترین برنده شد. ترتیب نوشتن پیشوندها مهم نیست./imagesXبا/images/جور نشد (اسلش آخر)، پس به/رسید. اگرlocation /images(بدون اسلش) مینوشتی،/imagesXو/images-oldرا هم میگرفت؛ معمولاً اسلش آخر را بنویس.
مثال ۲: تطبیق دقیق با =
Section titled “مثال ۲: تطبیق دقیق با =”= فقط با دقیقاً همان مسیر جور میشود و اگر جور شد، جستوجو همانجا تمام میشود (سریعترین نوع):
sed -i 's|^ location / {| location = /health {\n return 200 "exact = /health\\n";\n }\n location / {|' /etc/nginx/sites-available/shop.testgrep -A2 'location = ' /etc/nginx/sites-available/shop.testnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t /health /health/ /healthz '/health?full=1' location = /health { return 200 "exact = /health\n"; }nginx: configuration file /etc/nginx/nginx.conf test is successful/health -> 200 exact = /health/health/ -> 200 prefix //healthz -> 200 prefix //health?full=1 -> 200 exact = /health/health و /health?full=1 با = جور شدند (query string بخشی از مسیر نیست). ولی /health/ و /healthz نه؛ به / رسیدند. = برای مسیرهای پرتکرار و ثابت عالی است: صفحهی سلامت، favicon.ico، robots.txt.
مثال ۳: regex با ~ و ~*
Section titled “مثال ۳: regex با ~ و ~*”regex برای قانونهایی است که به پسوند یا الگو مربوطاند، نه به پوشه. ~ حساس به حروف است و ~* نیست:
sed -i 's|^ location / {| location ~* \\.(jpg\|jpeg\|png\|webp)$ {\n return 200 "regex ~* image\\n";\n }\n location ~ \\.php$ {\n return 200 "regex ~ php\\n";\n }\n location / {|' /etc/nginx/sites-available/shop.testgrep -A2 'location ~' /etc/nginx/sites-available/shop.testnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t /photo.jpg /PHOTO.JPG /index.php /INDEX.PHP /images/logo.png /images/products/42.jpg location ~* \.(jpg|jpeg|png|webp)$ { return 200 "regex ~* image\n"; } location ~ \.php$ { return 200 "regex ~ php\n"; }nginx: configuration file /etc/nginx/nginx.conf test is successful/photo.jpg -> 200 regex ~* image/PHOTO.JPG -> 200 regex ~* image/index.php -> 200 regex ~ php/INDEX.PHP -> 200 prefix //images/logo.png -> 200 regex ~* image/images/products/42.jpg -> 200 regex ~* image~*همjpgو همJPGرا گرفت؛~فقط.phpکوچک را (INDEX.PHPبه/رسید).- و نکتهی مهم:
/images/logo.pngو/images/products/42.jpgکه قبلاً به پیشوندهای/images/...میرفتند، حالا به regex رفتند! regex بر پیشوند معمولی برنده است، حتی بر بلندترین آن. این همان جایی است که بیشتر سردرگمیها شروع میشود.
مثال ۴: ^~، پیشوندی که regex را کنار میزند
Section titled “مثال ۴: ^~، پیشوندی که regex را کنار میزند”اگر میخواهی یک پیشوند حتماً برنده باشد (مثلاً همهی /images/products/ از یک جای خاص بیاید، چه پسوندش .jpg باشد چه نه)، ^~ بنویس:
sed -i 's| location /images/products/ {| location ^~ /images/products/ {|' /etc/nginx/sites-available/shop.testgrep -n 'location' /etc/nginx/sites-available/shop.testnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t /images/products/42.jpg /images/logo.png5: location = /health {8: location ~* \.(jpg|jpeg|png|webp)$ {11: location ~ \.php$ {14: location / {17: location /images/ {20: location ^~ /images/products/ {nginx: configuration file /etc/nginx/nginx.conf test is successful/images/products/42.jpg -> 200 prefix /images/products//images/logo.png -> 200 regex ~* imageحالا /images/products/42.jpg به ^~ /images/products/ رفت (regex بررسی نشد)، ولی /images/logo.png هنوز به regex میرود چون بلندترین پیشوندش (/images/) ^~ ندارد.
مثال ۵: کل ترتیب، در یک جدول
Section titled “مثال ۵: کل ترتیب، در یک جدول”فایل نهایی پنج location دارد. همهی حالتها را یکجا ببینیم:
t / /health /about /photo.JPG /index.php /images/logo.png /images/a.txt /images/products/42.jpg /images/products/list/ -> 200 prefix //health -> 200 exact = /health/about -> 200 prefix //photo.JPG -> 200 regex ~* image/index.php -> 200 regex ~ php/images/logo.png -> 200 regex ~* image/images/a.txt -> 200 prefix /images//images/products/42.jpg -> 200 prefix /images/products//images/products/list -> 200 prefix /images/products/بازخوانی با ترتیب Nginx:
| آدرس | =؟ |
بلندترین پیشوند | ^~؟ |
اولین regex جور | برنده |
|---|---|---|---|---|---|
/health |
✅ | = /health |
|||
/about |
/ |
— | / |
||
/photo.JPG |
/ |
~* تصویر |
regex | ||
/images/logo.png |
/images/ |
❌ | ~* تصویر |
regex | |
/images/a.txt |
/images/ |
❌ | — | /images/ |
|
/images/products/42.jpg |
/images/products/ |
✅ | (بررسی نمیشود) | ^~ |
مثال ۶: try_files، «اول این، نبود آن»
Section titled “مثال ۶: try_files، «اول این، نبود آن»”try_files فهرستی از جاهاست که Nginx به ترتیب امتحان میکند؛ اولین موجود را سرو میکند و اگر هیچکدام نبود، آخرین آیتم (یک کد خطا مثل =404، یا یک آدرس دیگر) اجرا میشود. حالا سایت را با فایل واقعی بازنویسی میکنیم:
mkdir -p /var/www/shop.test/docsecho '<h1>home</h1>' > /var/www/shop.test/index.htmlecho '<h1>about page</h1>' > /var/www/shop.test/about.htmlecho '<h1>docs index</h1>' > /var/www/shop.test/docs/index.htmlcat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; root /var/www/shop.test; index index.html;
location / { try_files $uri $uri.html $uri/ =404; }}EOFnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t / /about.html /about /docs/ /docs /nopenginx: configuration file /etc/nginx/nginx.conf test is successful/ -> 200 <h1>home</h1>/about.html -> 200 <h1>about page</h1>/about -> 200 <h1>about page</h1>/docs/ -> 200 <h1>docs index</h1>/docs -> 301 <html>/nope -> 404 <html>برای /about: اول $uri یعنی فایل /about (نبود)، بعد $uri.html یعنی /about.html (بود!) و همان سرو شد؛ پس آدرسهای «تمیز» بدون .html داری. $uri/ پوشه را امتحان میکند (و با index فایل داخلش). /nope با هیچکدام جور نشد و به =404 رسید.
مثال ۷: root در برابر alias
Section titled “مثال ۷: root در برابر alias”هر دو مشخص میکنند فایلها کجا هستند، ولی متفاوت حساب میکنند:
root: مسیر کامل درخواست را به انتهای پوشه اضافه میکند.location /static/ { root /srv; }و درخواست/static/app.css←/srv/static/app.css.alias: بخشی از مسیر را که با location جور شده جایگزین میکند.location /static/ { alias /srv/assets/; }و درخواست/static/app.css←/srv/assets/app.css.
mkdir -p /srv/assets /srv/staticecho 'from /srv/assets' > /srv/assets/app.cssecho 'from /srv/static' > /srv/static/app.csscat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; root /var/www/shop.test;
location /static/ { root /srv; } location /assets-by-alias/ { alias /srv/assets/; }}EOFnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t /static/app.css /assets-by-alias/app.cssnginx: configuration file /etc/nginx/nginx.conf test is successful/static/app.css -> 200 from /srv/static/assets-by-alias/app.css -> 200 from /srv/assetsقاعدهی سرانگشتی: اگر اسم پوشه روی دیسک با اسم مسیر یکی است، root (سادهتر)؛ اگر فرق دارد، alias (و آنوقت اسلشها را دقیق بنویس؛ اشتباهات رایج).
پشت پرده: $uri، نرمالسازی و redirect داخلی
Section titled “پشت پرده: $uri، نرمالسازی و redirect داخلی”Nginx قبل از تطبیق location، مسیر را نرمال میکند: %XX ها را باز میکند، اسلشهای تکراری را یکی و .. را حل میکند. نتیجه در متغیر $uri است (و مسیر خام در $request_uri). برای همین با ../ نمیشود از یک location فرار کرد:
cat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; location / { return 200 "uri=$uri request_uri=$request_uri\n"; }}EOFnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t '/a//b///c' '/a/b/../c' '/caf%C3%A9' '/x/./y?q=1'nginx: configuration file /etc/nginx/nginx.conf test is successful/a//b///c -> 200 uri=/a/b/c request_uri=/a//b///c/a/b/../c -> 200 uri=/a/c request_uri=/a/b/../c/caf%C3%A9 -> 200 uri=/café request_uri=/caf%C3%A9/x/./y?q=1 -> 200 uri=/x/y request_uri=/x/./y?q=1curl --path-as-is (داخل t) مسیر را دستنخورده فرستاد و Nginx خودش نرمالش کرد: /a//b///c ← /a/b/c، /a/b/../c ← /a/c، %C3%A9 ← é.
دومین رفتار پنهان، redirect داخلی (internal redirect) است. وقتی Nginx برای پوشهی / فایل index.html را پیدا میکند، درخواست را داخلی به /index.html تبدیل میکند و دوباره از اول location ها را میگردد. این میتواند غافلگیرت کند:
cat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; root /var/www/shop.test; index index.html;
location = / { add_header X-Matched "exact /" always; } location ~ \.html$ { add_header X-Matched "regex html" always; }}EOFnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1curl -s -i http://shop.test/ | grep -iE '^x-matched|<h1>'nginx: configuration file /etc/nginx/nginx.conf test is successfulX-Matched: regex html<h1>home</h1>درخواست / اول با location = / جور شد، ولی چون آن location فقط پوشه را دارد، ماژول index آن را به /index.html تبدیل کرد که دوباره گشته شد و این بار با regex html جور شد؛ هدر نهایی از location دوم آمد. try_files با آیتم آخری که یک آدرس است (مثل /index.html) و error_page هم همین redirect داخلی را دارند.
جدولهای مرجع
Section titled “جدولهای مرجع”ترتیب انتخاب (به خاطر بسپار):
| قدم | چه میکند |
|---|---|
| ۱ | اگر location = ... دقیقاً جور شد ← برنده، تمام |
| ۲ | بلندترین location پیشوندی (معمولی یا ^~) جور شده را پیدا و به خاطر بسپار |
| ۳ | اگر آن پیشوند ^~ داشت ← برنده، تمام |
| ۴ | regex ها (~ و ~*) را به ترتیب نوشتن امتحان کن؛ اولین جور ← برنده |
| ۵ | هیچ regex جور نشد ← همان پیشوند قدم ۲ |
شکلهای try_files:
| نوشتن | کاربرد |
|---|---|
try_files $uri $uri/ =404; |
سایت استاتیک ساده |
try_files $uri $uri.html $uri/ =404; |
آدرسهای بدون .html |
try_files $uri /index.html; |
SPA (هر مسیری که فایل نیست به اپ برسد؛ درس بعد) |
try_files $uri @app; |
اگر فایل نبود، به named location (مثلاً proxy به اپ) |
root و alias:
root /srv; |
alias /srv/assets/; |
|
|---|---|---|
در location /static/ درخواست /static/a.css |
/srv/static/a.css |
/srv/assets/a.css |
| کجا مجاز | http، server، location |
فقط location |
| ریسک | کم | اسلشهای ناهمخوان (باگ امنیتی) |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) alias با اسلش ناهمخوان (باگ امنیتی «off-by-slash»)
Section titled “۱) alias با اسلش ناهمخوان (باگ امنیتی «off-by-slash»)”این یکی از معروفترین باگهای تنظیمات Nginx است. location بدون اسلش آخر و alias با اسلش:
mkdir -p /srv/uploads && echo 'user photo' > /srv/uploads/pic.txtecho 'DB_PASSWORD=secret' > /srv/config.envcat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; location /uploads { alias /srv/uploads/; }}EOFnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t /uploads/pic.txt /uploads../config.envnginx: configuration file /etc/nginx/nginx.conf test is successful/uploads/pic.txt -> 200 user photo/uploads../config.env -> 200 DB_PASSWORD=secret/uploads../config.env با پیشوند /uploads جور است؛ Nginx /uploads را با /srv/uploads/ جایگزین کرد و نتیجه شد /srv/uploads/../config.env یعنی /srv/config.env: فایل رمز از بیرون پوشهی آپلود خوانده شد! راهحل: اسلش آخر location و alias را هماهنگ کن (هر دو با اسلش: location /uploads/ { alias /srv/uploads/; })، یا اگر اسمها یکیاند، root بنویس.
sed -i 's|location /uploads {|location /uploads/ {|' /etc/nginx/sites-available/shop.testnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t /uploads/pic.txt /uploads../config.envrm /srv/config.envnginx: configuration file /etc/nginx/nginx.conf test is successful/uploads/pic.txt -> 200 user photo/uploads../config.env -> 404 <html>حالا /uploads../ با /uploads/ جور نمیشود و به هیچ location ای نمیرسد (و چون root پیشفرض فایلی ندارد، 404).
۲) regex ای که پیشوند را میخورد
Section titled “۲) regex ای که پیشوند را میخورد”مثال ۳: یک location ~* \.(png|jpg)$ باعث شد عکسهای /images/ دیگر از location /images/ سرو نشوند. راهحل: اگر پیشوندی باید برنده باشد، ^~.
۳) ترتیب regex ها
Section titled “۳) ترتیب regex ها”cat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; location ~ \.(js|css)$ { return 200 "generic static regex\n"; } location ~ ^/admin/.*\.js$ { return 200 "admin js regex\n"; }}EOFnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t /admin/app.jsnginx: configuration file /etc/nginx/nginx.conf test is successful/admin/app.js -> 200 generic static regexبین regex ها اولین جور برنده است، نه «دقیقترین». قانون admin هیچوقت اجرا نمیشود. راهحل: regex های خاصتر را بالاتر بنویس، یا برای پوشهها ^~ به کار ببر.
۴) location /api بدون اسلش
Section titled “۴) location /api بدون اسلش”location /api با /api/users جور است، ولی با /apiary و /api-docs هم. راهحل: location /api/ (و اگر /api بدون اسلش هم باید کار کند، یک location = /api { return 301 /api/; }).
۵) try_files بدون آیتم آخر مناسب
Section titled “۵) try_files بدون آیتم آخر مناسب”try_files $uri $uri/; بدون =404 یا آدرس جایگزین، اگر هیچکدام نبود، آخرین آیتم ($uri/) را بهعنوان redirect داخلی امتحان میکند و ممکن است به پاسخ عجیب (مثل ۵۰۰ یا ۳۰۱ بیدلیل) برسی. راهحل: همیشه آخرین آیتم یک کد (=404) یا یک آدرس/named location مشخص باشد.
برای سایت shop.test یک location = /robots.txt بساز که بدون هیچ فایلی، متن User-agent: * و Disallow: /admin/ را برگرداند، و یک location = /favicon.ico که با کد 204 (بدون محتوا) جواب دهد و در لاگ دسترسی ثبت نشود (access_log off;). با curl -i آزمایش کن.
دیدن جواب
cat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; root /var/www/shop.test;
location = /robots.txt { default_type text/plain; return 200 "User-agent: *\nDisallow: /admin/\n"; } location = /favicon.ico { access_log off; return 204; }}EOFnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1curl -s -i http://shop.test/robots.txt | grep -vE '^(Date|Server|Connection):'echo "==="curl -s -i http://shop.test/favicon.ico | head -n 1grep -c 'favicon' /var/log/nginx/access.lognginx: configuration file /etc/nginx/nginx.conf test is successfulHTTP/1.1 200 OKContent-Type: text/plainContent-Length: 32
User-agent: *Disallow: /admin/===HTTP/1.1 204 No Content0default_type text/plain نوع پاسخ را تعیین کرد (return با متن، بهصورت پیشفرض نوع application/octet-stream میگیرد). access_log off باعث شد درخواستهای favicon در لاگ نیایند (شمارش ۰).
تمرین اصلی درس: برای shop.test سه رفتار جدا بساز: /api/ به یک سرور پایتون روی 127.0.0.1:9101 برود (reverse proxy)، /static/ از پوشهی /srv/assets/ با alias سرو شود، و بقیهی مسیرها از /var/www/shop.test با try_files (و 404 اگر فایل نبود). ثابت کن هر کدام از جای درست جواب میدهد.
دیدن جواب
mkdir -p /srv/lx-api/apiecho '{"products": 3}' > /srv/lx-api/api/products(cd /srv/lx-api && exec python3 -m http.server 9101 --bind 127.0.0.1) > /dev/null 2>&1 &echo 'body { color: teal; }' > /srv/assets/app.csscat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; root /var/www/shop.test; index index.html;
location /api/ { proxy_pass http://127.0.0.1:9101; } location /static/ { alias /srv/assets/; } location / { try_files $uri $uri/ =404; }}EOFsleep 1nginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t /api/products /static/app.css / /about.html /missingcurl -s -I http://shop.test/api/products | grep -i '^content-type'nginx: configuration file /etc/nginx/nginx.conf test is successful/api/products -> 200 {"products": 3}/static/app.css -> 200 body { color: teal; }/ -> 200 <h1>home</h1>/about.html -> 200 <h1>about page</h1>/missing -> 404 <html>Content-Type: application/octet-stream/api/products از پایتون آمد (پایتون فایل /srv/lx-api/api/products را فرستاد؛ چون proxy_pass بدون مسیر، کل آدرس را همانطور میفرستد)، /static/app.css با alias از /srv/assets/، و بقیه از root سایت. جزئیات proxy_pass (اسلش آخر، هدرها) در درس reverse proxy.
پیشبینی کن، بعد آزمایش کن. با این تنظیمات، هر یک از آدرسهای زیر به کدام location میرسد؟ اول جواب را روی کاغذ بنویس و بعد با t درستیاش را بسنج:
/، /docs، /docs/intro، /docs/img/a.PNG، /media/v.mp4، /media/a.png.
location = /docs { return 200 "A\n"; }location /docs/ { return 200 "B\n"; }location ^~ /media/ { return 200 "C\n"; }location ~* \.(png|jpg)$ { return 200 "D\n"; }location / { return 200 "E\n"; }دیدن جواب
cat > /etc/nginx/sites-available/shop.test <<'EOF'server { listen 80; server_name shop.test; location = /docs { return 200 "A\n"; } location /docs/ { return 200 "B\n"; } location ^~ /media/ { return 200 "C\n"; } location ~* \.(png|jpg)$ { return 200 "D\n"; } location / { return 200 "E\n"; }}EOFnginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1t / /docs /docs/intro /docs/img/a.PNG /media/v.mp4 /media/a.pngnginx: configuration file /etc/nginx/nginx.conf test is successful/ -> 200 E/docs -> 200 A/docs/intro -> 200 B/docs/img/a.PNG -> 200 D/media/v.mp4 -> 200 C/media/a.png -> 200 C/ ← E (فقط پیشوند /). /docs ← A (دقیق). /docs/intro ← B (بلندترین پیشوند، هیچ regex جور نیست). /docs/img/a.PNG ← D (پیشوند /docs/ بدون ^~ است، پس regex بررسی شد و ~* حروف بزرگ را هم گرفت). /media/v.mp4 ← C. /media/a.png ← C (چون ^~ جلوی بررسی regex را گرفت). اگر همه را درست حدس زدی، ترتیب location ها را بلدی.
آزمونک
Section titled “آزمونک”هم location /images/ هست و هم location ~* \.png$. درخواست /images/logo.png به کدام میرسد؟
بلندترین پیشوند فقط به خاطر سپرده میشود؛ اگر regex ای جور شد، regex برنده است.
location = /health با کدام درخواست جور میشود؟
query string جزو مسیر تطبیق نیست؛ = فقط دقیقاً /health.
دو regex داری: اولی ~ \.js$ و دومی ~ ^/admin/.*\.js$. درخواست /admin/app.js به کدام میرسد؟
regex خاصتر را بالاتر بنویس.
در location /static/ { root /srv/web; } درخواست /static/a.css کدام فایل را میخواند؟
root کل مسیر را به انتها اضافه میکند؛ alias بخش جورشده را جایگزین میکند.
location /files { alias /srv/files/; } چه خطری دارد؟
اسلش آخر location و alias را هماهنگ کن.
try_files $uri $uri.html $uri/ =404; برای درخواست /about وقتی about.html وجود دارد چه میکند؟
try_files به ترتیب امتحان میکند و اولین موجود را سرو میکند.
چرا با location ^~ /media/ درخواست /media/a.png به regex تصویرها نمیرسد؟
برای پوشههایی که باید حتماً برنده باشند.
جمعبندی
Section titled “جمعبندی”- location رفتار هر مسیر را داخل یک server block تعیین میکند. انواع:
=(دقیق)،^~(پیشوندی با اولویت)،~/~*(regex حساس/غیرحساس به حروف)، و پیشوندی معمولی. - ترتیب:
=← بلندترین پیشوند (به خاطر سپرده) ← اگر^~بود تمام ← regex ها به ترتیب نوشتن ← وگرنه همان پیشوند. regex بر پیشوند معمولی برنده است. location /x/(با اسلش) دقیقتر از/xاست؛location = /pathبرای مسیرهای ثابت پرتکرار.try_files: جاها را به ترتیب امتحان میکند؛ آخرین آیتم یک کد (=404) یا آدرس/@namedباشد.rootمسیر کامل را اضافه میکند؛aliasبخش جورشده را جایگزین میکند؛ اسلشها را هماهنگ کن (باگ off-by-slash).- Nginx مسیر را نرمال میکند (
$uri)؛indexوtry_filesوerror_pageباعث redirect داخلی و جستوجوی دوبارهی location ها میشوند. - برای دیباگ:
return 200 "اسم location"یاadd_header X-Matched ....
| دستور | کاری که میکند |
|---|---|
location = /health { return 200 "ok\n"; } | تطبیق دقیق |
location /api/ { proxy_pass http://127.0.0.1:3000; } | پیشوندی: همهی /api/... |
location ^~ /static/ { ... } | پیشوندی که regex را کنار میزند |
location ~* \.(jpg|png|webp)$ { ... } | regex بدون حساسیت به حروف |
try_files $uri $uri/ =404; | سایت استاتیک |
try_files $uri $uri.html $uri/ =404; | آدرس بدون .html |
try_files $uri @app; | نبود؟ برو به named location |
location /static/ { root /srv; } | /static/a ← /srv/static/a |
location /static/ { alias /srv/assets/; } | /static/a ← /srv/assets/a (اسلشها هماهنگ) |
add_header X-Matched "name" always; | کدام location جواب داد؟ |
curl --path-as-is "http://site/a/../b" | فرستادن مسیر بدون نرمالسازی curl |