طرح‌چه

ریدایرکت (redirect) و ریرایت (rewrite) | Nginx from scratch

۲۴ تیر ۱۴۰۴

ریدایرکت (redirect) و ریرایت (rewrite) | Nginx from scratch
دستورهای rewrite و redirect معمولاً برای ساخت URL های با معنی تر، یا انتقال کاربران به آدرس‌های جدید استفاده می‌شوند.

با استفاده از دستورهای rewrite و return میتوان با حفظ آدرس فعلی, محتوای متفاوتی را به کاربر نمایش داد یا اینکه کاربر را به آدرس جدیدی انتقال داد.

دستور return

دستور return باعث خاتمه پردازش درخواست و برگرداندن استاتوس کد به کاربر میشود (مستندات). از این دستور در بلوک های if , ‍location و server میتوان استفاده کرد.

با استفاده از یکی از کدهای وضعیت 301، 302، 303، 307 یا 308 می‌توان یک URL را به‌عنوان پارامتر دوم مشخص کرد تا کاربر به آن آدرس هدایت شود (ریدایرکت انجام شود). در صورتی که از کد وضعیت دیگری استفاده شود، میتوان محتوای دلخواه را به‌عنوان پارامتر دوم ارسال کرد تا در پاسخ به کاربر، به‌عنوان بدنه ی پاسخ (body) در نظر گرفته شود.

server {
    listen 8065 default_server;

    location / {
        return 301 /home;
    }

    location /downloads {
        return 404;
    }

    location /home {
        default_type text/html;

        return 200 '<h1>This is our home page</h1>';
    }
}

در مثال بالا اگر یکی از آدرس ها ‍localhost:8065 یا 127.0.0.1:8065 را در مرورگر باز کنید باید بلافاصله به مسیر /home هدایت می شوید و سپس پاسخی که مشخص کردیم نمایش داده شود. همچنین اگر به مسیر /downloads بروید با استاتوس کد 404 مواجه می شوید.

دستور rewrite

با استفاده از دستور rewrite علاوه بر ریدایرکت کردن به آدرس جدید, میتوان با حفظ آدرس فعلی درخواست را به مسیر دیگری فرستاد.

ساختار کلی این دستور به صورت زیر است (مستندات):

rewrite regex replacement [flag];

همونطور که میبینم این دستور به عنوان پارامتر اول یک پترن از نوع PCRE قبول میکنه , پارامتر دوم مسیری هست که میخوایم جایگزین بشه و در آخر از یک flag در صورت نیاز میتوان استفاده کرد.

استفاده از فلگ (flag) باعث می‌شود پردازش دستورالعمل‌های فعلی قطع شود و ادامه پیدا نکند. فلگ (flag) هایی که میتونیم استفاده کنیم به صورت لیست زیر هستند:

  • redirect
  • permanent
  • last
  • break

فلگ redirect

در صورت استفاده از redirect , یک ریدایرکت با کد استاتوس 302 انجام خواهد شد. این دستور در زمانی استفاده می شود که مسیری که عنوان replacement مشخص میکنیم با http:// , https:// یا $scheme شروع نشده باشد.

اگر مسیری که به عنوان replacement مشخص میکنیم با https:// , ‍http:// و یا $scheme شروع شود, نتیجه همانند استفاده از فلگ ‍redirect خواهد بود و نیازی به استفاده از این فلگ نیست.

server {
    listen 8069 default_server;

    rewrite_log on;

    location /hello-world/ {
        alias /var/www/public/;
        index index.html;
    }

    rewrite ^/hello-man/?$ /hello-world/ redirect;
    
    rewrite ^/hello-woman/?$ http://localhost:8067/hello-world/;
}

در مثال بالا با استفاده از دستور rewrite_log لاگ مربوط به دستورات rewrite را فعال کردیم. اگر به مسیر /hello-man بریم, به همراه استاتوس کد 302 به مسیر /hello-world هدایت میشویم. بدین معنی که در مرورگر URL از /hello-man به /hello-world تغییر خواهد کرد.

همچنین در صورتی که به آدرس /hello-woman بروید به مسیر hello-world هدایت میشید. در اینجا در آدرس http:// وجود دارد که باعث می شود ریدایرکت به همراه استاتوس کد 302 انجام شود.

با استفاده از rewrite میتوان ترافیک مربوط به یک مسیر را به مسیر دیگری هدایت کرد به مثال زیر دقت کنید:

server {
    listen 8069 default_server;

    rewrite_log on;

    location /bye {
        default_type text/html;

        return 200 '<h1>Bye bye</h1>';
    }

    location /welcome {
        default_type text/html;

        return 200 '<h1>Welcome to the world of Nginx</h1>';
    }

    rewrite ^/welcome/?$ /bye/ redirect; 
}

در مثال بالا ترافیک مربوط به درخواست های /welcome را روی /bye ریدایرکت کردیم. در صورتی که کاربر به مسیر /welcome برود آدرس در مرورگر به /bye تغییر خواهد کرد.

فلگ permanent

این فلگ رفتاری مانند فلگ redirect دارد با این تفاوت که استاتوس کد به 301 تغییر خواهد کرد. در صورتی که بخواهید یک ریدایرکت دائمی را معرفی کنید, بهتر است از این فلگ استفاده کنید.

server {
    listen 8069 default_server;

    rewrite_log on;

    location /hello-world/ {
        alias /var/www/public/;
        index index.html;
    }

    rewrite ^/hello-man/?$ /hello-world/ permanent;
    
    rewrite ^/hello-woman/?$ http://localhost:8067/hello-world/ permanent;
}

تفاوت فلگ های redirect و permenant در استاتوس کدی هست که هنگام ریدایرکت برمیگردانند.

فلگ last

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

برخلاف فلگ های redirect و permanent , هنگامی که از فلگ last استفاده میکنید آدرس مرورگر کاربر تغییری نمیکند بلکه بازنویسی آدرس در داخل nginx رخ میدهد. یعنی با حفظ آدرس وارد شده در مرورگر , به درخواست از آدرس دیگری پاسخ داده می شود.

server {
    listen 8066 default_server;

    rewrite_log on;

    rewrite ^/hello-man/?$ /hello-world/ last;

    location /hello-world/ {
        alias /var/www/public/;
        index index.html;
    }
}

با توجه به مثال بالا اگر در مرورگر خود به مسیر /hello-man بروید, با حفظ آدرس , اطلاعات از مسیر ‍/hello-world برای شما لود خواهد شد.

server {
    listen 8066 default_server;

    rewrite_log on;

    rewrite ^/hello-man/?$ /hello-world/ last;
    rewrite ^/hello-world/?$ /bye/ last;

    location /hello-world/ {
        alias /var/www/public/;
        index index.html;
    }

    location /bye {
        default_type text/html;

        return 200 '<h1>Bye bye</h1>';
    }
}

با توجه به مثال بالا اگر به ادرس /hello-man برویم, این آدرس با اولین rewrite تطبیق داده میشود و چون این rewrite دارای فلگ است (در اینجا last) باقی rewrite هایی که در بلاک فعلی هستن (در اینجا server) تطبیق داده نخواهند شد و درخواست به /hello-world بازنویسی میشود. سپس درخواست دوباره همانند یک درخواست جدید بازبینی می شود و چون آدرس به /hello-world تغییر کرده است دوباره rewrite دوم تطبیق داده میشود و درخواست به مسیر /bye بازنویسی می شود و در نهایت روی صفحه ‍‍Bye bye را خواهیم دید.

فلگ break

این فلگ باعث بازنویسی آدرس و توقف اجرای سایر دستورات rewrite اما برخلاف last تطبیق جدیدی با location ها انجام نمی دهد. بنابراین اگر این فلگ در داخل یک بلاک location استفاده شده باشد پردازش درخواست درون همان location ادامه خواهد یافت و تطبیق جدیدی انجام نخواهد شد.

server {
    listen 8066 default_server;

    rewrite_log on;

    rewrite ^/hello-man/?$ /hello-world/ break;
    rewrite ^/hello-world/?$ /bye/ break;

    location /hello-world/ {
        alias /var/www/public/;
        index index.html;
    }

    location /bye {
        default_type text/html;

        return 200 '<h1>Bye bye</h1>';
    }
}

با توجه به مثال بالا اگر آدرس ‍127.0.0.1:8066/hello-man را درون مرورگر باز کنیم, اولین rewrite انجام خواهد شد که باعث میشود باقی rewrite ها پردازش نشوند. و از آنجایی که تطبیق جدیدی با آدرس بازنویسی شده انجام نمی شود, هیچگاه rewrite دوم انجام نمیشود و در نهایت درخواست از طریق مسیر /hello-world پردازش خواهد شد.

استفاده از rewrite بدون فلگ

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

server {
    listen 8067 default_server;

    rewrite_log on;

    location /goodbye1 {
        rewrite ^/goodbye/?$ /hello-world/;
        rewrite ^/hello-world/?$ /bye/;
        rewrite ^/bye/?$ /welcome-madam/;
    }
    
    location /goodbye2 {
        rewrite ^/goodbye/?$ /hello-world/;
        rewrite ^/hello-world/?$ /bye/;
        rewrite ^/bye/?$ /welcome-madam/;

        default_type text/html;

        return 200 '<h1>Goodbye</h1>';
    }    

    location /welcome {
        default_type text/html;

        return 200 '<h1>Welcome to the world of Nginx</h1>';
    }

    rewrite ^/welcome-madam/?$ /welcome/;
}

در مثال بالا اگر به آدرس /goodbye1 برویم درخواست از مسیر /welcome برای ما لود خواهد شد. اما اگر به ادرس /goodbye2 برویم متن ‍goodbye از همان مسیر برایمان درون صفحه نمایش داده میشود.

دستور set

با استفاده از دستور set میتوان یک متغیر ایجاد کرد و آن را مقداردهی کرد. متغیرها در nginx با علامت دلار $ مشخص میشوند. برای ایجاد متغیر میتوان مانند مثال زیر عمل کرد:

set $name mahdi;

مقدار داخل متغیرها همیشه از نوع متنی در نظر گرفته خواهند شد. در صورتی که مقدار مورد نظر شما دارای کاراکتر فاصله یا ; باشد, باید مقدار مورد نظر خود را با کوتیشن یا دابل کوتیشن محصور کنید.

set $name "John Doe";

دستور if

با استفاده از دستور if میتوان در صورت برقرار بودن یک شرط, کدی را اجرا کرد. ساختار کلی آن به صورت زیر است:

if(condition) {
	...
}

در اینجا condition شرط مورد نظر ما است و در صورتی که برابر با true شود, کدهای درون بدنه if اجرا خواهند شد. (مستندات)

  • برای بررسی برابر بودن میتوان از = و یا نابرابر بودن از ! = استفاده کرد.
  • میتوان از ~ و یک Regex برای برابر بودن استفاده کرد. در این حالت به بزرگ یا کوچک بودن حروف حساس است.
  • میتوان از *~ و یک Regex برای برابر بودن استفاده کرد. در این حالت به بزرگ و کوچک بودن حروف حساس نیست.
  • در صورتی که از متغیر استفاده کرد و آن متغیر دارای مقداری نباشد یا دارای ‍0 باشد از آن به صورت false نتیجه گیری خواهد شد.

در اکثر موارد از چهار حالت بالا برای مقایسه استفاده میکنیم. در مورد حالت های پیچیده تر را میتوانید در مستندات Nginx مطالعه کنید.

server {
    listen 8070 default_server;

    rewrite_log on;

    set $name $arg_name;

    location / {
        if ($name = "") {
            set $name "world";
        }

        default_type text/html;
        
        return 200 "<h1>Hello $name</h1>";
    }
}

با توجه به کد بالا, اگر به آدرس, ?name= را اضافه کنید و به آن مقدار دهید, آن مقدار درون صفحه نمایش داده میشود. در غیر اینصورت متن hello world درون صفحه نمایش داده میشود. برای مثال اگر به آدرس 127.0.0.1:8070?name=mahdi برویم متن Hello mahdi بر روی صفحه نمایش داده میشود.

در این کد ابتدا یک متغیر با نام $name تعریف کردیم و با استفاده از $arg_name مقدار query string که نام آن name باشد را درون این متغیر قرار دادیم. سپس درون بدنه ی location در صورتی که $name خالی باشد به صورت پیش فرض مقدار world‍‍ را درون آن قرار میدهیم. بدین صورت اگر نامی را با استفاده کوئری استرینگ پاس ندهیم Hello world نمایش داده میشود و در غیر اینصورت Hello به همراه نام مورد نظر ما بر روی صفحه نمایش داده میشود.

در Nginx یکسری متغیر پیش فرض وجود دارند که میتوانید لیست آنها را درون مستندات ببینید و در صورت نیاز میتوانید مستندات را بررسی کنید و از متغیر دلخواه استفاده کنید.

به کدهایی که در این قسمت بررسی کردیم رو میتونید از طریق رپازیتوری گیت هاب دسترسی داشته باشید.


قسمت قبل: کش (cache) | Nginx from scratch

قسمت بعد: ایجاد پسوورد بر روی یک مسیر | Nginx from scratch


دیدگاه ها