Примеры редиректов в NGINX

Thank you for reading this post, don't forget to subscribe!

Настройка перенаправлений

Настрой­ки необ­хо­ди­мо вно­сить в фай­лах кон­фи­гу­ра­ций вир­ту­аль­ных доме­нов. В Linux на осно­ве RPM (CentOS, Red Hat), как пра­ви­ло, они рас­по­ло­же­ны в дирек­то­рии /etc/nginx/conf.d/. В Linux на осно­ве Deb (Ubuntu, Debian) — в дирек­то­рии /etc/nginx/sites-enabled/. Во FreeBSD все в одном фай­ле — /usr/local/etc/nginx/nginx.conf.

Саму настрой­ку на пере­на­прав­ле­ние в NGINX мож­но про­пи­сать несколь­ки­ми способами.

1. Пер­вый:

rewrite ^ https://$host$request_uri? <флаг>;

$host — имя хоста из запро­са, если отсут­ству­ет — имя в поле «Host» заго­лов­ка, если тоже отсут­ству­ет — имя сер­ве­ра; $request_uri — пер­во­на­чаль­ный запрос с аргу­мен­та­ми (все, что идет после домен­но­го имени).
** где фла­ги могут быть следующие:

  • permanent — пере­на­прав­ле­ние с кодом 301.
  • redirect — пере­на­пра­вить с кодом 302.
  • last — закон­чить обра­бот­ку с пере­хо­дом в новый location.
  • break — закон­чить обра­бот­ку и остать­ся в теку­щем location.

2. Вто­рой:

return <код> https://$host$request_uri;

* где коды могут исполь­зо­вать­ся любые, но чаще все­го — 301, 302, 404.

Есть раз­лич­ные мне­ния, какой из мето­дов луч­ше и без­опас­нее, поэто­му каким вос­поль­зо­вать­ся — решать по ситу­а­ции. В дан­ных при­ме­рах исполь­зу­ют­ся оба варианта.

После вне­се­ния изме­не­ний, необ­хо­ди­мо про­ве­рить их корректность:

nginx -t

И для их при­ме­не­ния пере­за­пу­стить веб-сервер:

systemctl restart nginx

service nginx restart

* в пер­вом при­ме­ре пере­за­пуск выпол­ня­ет­ся на новых систе­мах Linux. Вто­рой при­мер — на уста­рев­ших или FreeBSD.

Про­ве­ряя реди­рек­ты в бра­у­зе­ре, сле­ду­ет учесть, что настрой­ки могут кэши­ро­вать­ся. Для обнов­ле­ния кэша исполь­зуй­те ком­би­на­цию Ctrl + F5. Если и это не помо­га­ет, закры­вай­те вклад­ку и откры­вай­те новую.

С HTTP на HTTPS (другой порт)

При­мер кон­фи­гу­ра­ции для пере­на­прав­ле­ния запро­сов на дру­гой порт — с 80 (http) на 443 (https):

server {
listen 80;
server_name domain.ru www.domain.ru;
return 301 https://$host$request_uri;
}

* в дан­ном при­ме­ре для всех обра­ще­ний к сай­ту domain.ru по 80 пор­ту (http) будет рабо­тать реди­рект на 443 порт (https) с кодом 301 (для скле­и­ва­ния доменов).

С одного домена на другой

server {

server_name domain1.ru;
return 302 http://domain2.ru$request_uri;
}

C домена без www на домен с www

server {

server_name domain.ru;
return 301 http://www.$host$request_uri;
}

С www на без www

server {

server_name "~^www\.(.*)$" ;
return 301 $scheme://$1$request_uri;
}

C index.php на / (корень)

Дан­ная настрой­ка поз­во­лит пере­ве­сти все запро­сы с /index.php на кор­не­вой адрес /:

server {

if ($request_uri ~ "^(.*)index\.(?:php|html)") {
return 301 $1;
}
}

Перенаправление запросов для отсутствующих доменов (перенаправление по умолчанию)

Если обра­ще­ние к веб-сер­ве­ру идет по IP-адре­су или доме­ну, кото­рый не про­пи­сан в кон­фи­гу­ра­ци­он­ном фай­ле, мож­но пере­на­пра­вить весь тра­фик на домен по умолчанию:

server {
listen 80 default_server;
return 302 https://welcome.domain.ru$request_uri;
}

или неза­ви­си­мо от протокола:

server {
listen 80 default_server;
return 302 $scheme://welcome.domain.ru$request_uri;
}

server {
listen 443 default_server;
return 302 $scheme://welcome.domain.ru$request_uri;

ssl on;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/cert.key;
}

$scheme поз­во­ля­ет пере­ве­сти запрос на тот же про­то­кол (http или https), по кото­ро­му он был инициирован.
* если nginx дол­жен слу­шать и обра­ба­ты­вать запро­сы по https, необ­хо­ди­мо ука­зы­вать в настрой­ках пути к сертификатам.

С IP-адреса на домен

В дан­ном слу­чае мы пере­во­дим все запро­сы по IP-адре­су на кон­крет­ный домен:

server {
listen 80;
server_name 192.168.1.15;
return 301 http://site.ru$request_uri;
}

* при отправ­ке http-запро­са на сер­вер 192.168.1.15 по IP-адре­су, он будет пере­ве­ден на домен site.ru.

Редирект домена и всех его поддоменов

server {

server_name  domain domain.*;
return 301 https://$host$request_uri;
}

На другой файл

Это ско­рее не пере­на­прав­ле­ние, а али­ас или rewrite. Поз­во­ля­ет по запро­су одно­го из фай­лов, отдать другой:

server {

location = /robots.txt {
rewrite ^/robots.txt$ /robots2.txt;
}
}

* в дан­ном при­ме­ре по запро­су robots.txt, сер­вер отдаст содер­жи­мое robots2.txt.

Часть url на другой сервер

Пере­на­пра­вить запрос на дру­гой сер­вер при обра­ще­нии по url page1:

server {

server_name  domain1.ru;
location  ~ ^/page1/(.*)$ {
return 301 $scheme://domain2.ru/$1;
}
}

* в дан­ном при­ме­ре для всех запро­сов, начи­на­ю­щих­ся на /page1/… будет рабо­тать пере­на­прав­ле­ние на дру­гой домен domain2.ru.

Редирект со слешем

1. Убрать слеш в кон­це url

а) с помо­щью rewrite:

server {

rewrite ^/(.*)/$ /$1 permanent;
}

б) с помо­щью return:

server {

if ($request_uri ~ "^(.*)/$") {
return 301 /$1;
}
}

2. Доба­вить слеш в кон­це url

а) с помо­щью rewrite:

server {

rewrite ^(.*[^/])$ $1/ permanent;
}

б) с помо­щью return:

server {

if ($request_uri ~ "^(.*)[^/]$") {
return 301 $1/;
}
}

На другую страницу

Нам может пона­до­бить­ся пере­на­прав­лять запро­сы с одной стра­ни­цы сай­та на дру­гую. При­ве­дем при­ме­ры, как это сде­лать с помо­щью return и rewrite.

а) с помо­щью rewrite:

server {

rewrite ^/page1$ /page2 permanent;
}

б) с помо­щью return:

server {

location = /page1 {
return 301 /page2;
}
}

Удалить часть URL

Ино­гда нуж­но уда­лять часть url. Это мож­но сде­лать сле­ду­ю­щи­ми способами:

server {

rewrite /deleted-url/(.*) /$1 permanent;
}

или:

server {

if ($request_uri ~ "/deleted-url/(.*)") {
return 301 $1;
}
}

* в дан­ном при­ме­ре из url мы уда­лим deleted-url/.

Перенаправить запрос в случае обращения к несуществующим файлам

Пред­по­ло­жим, что нам нуж­но обра­щать­ся к скрип­там на сер­ве­ре, но без про­пи­сы­ва­ния в URL .php на кон­це. Запрос будет выгля­деть, при­мер­но, http://url/. Что­бы nginx пере­ки­нул запрос на url.php встав­ля­ем следующее:

server {

if (!-e $request_filename){
rewrite ^(.*)$ /$1.php;
}
}

* в дан­ном при­ме­ре мы про­ве­ря­ем нали­чие фай­ла, к кото­ро­му идет обра­ще­ние. И если его нет, то про­ис­хо­дит заме­на адре­са на такое же имя фай­ла с .php на конце.

Проксирование

Прок­си­ро­ва­ние, в отли­чие от реди­рек­та, не пере­да­ет инструк­ции бра­у­зе­ру перей­ти на дру­гой url — NGINX сам выпол­ня­ет http-запрос по дру­го­му адре­су и воз­вра­ща­ет гото­вый ответ. Эта воз­мож­ность может при­ме­нять­ся для внут­рен­не­го рас­пре­де­ле­ния сер­вер­ных ресурсов.

Хоть это и не совсем реди­рект, рас­смот­рим при­ме­ры его настрой­ки, так как очень часто нуж­но не пере­на­прав­ле­ние, а, как раз, обрат­ное проксирование.

1. На другой сервер

При­мер внут­рен­не­го пере­на­прав­ле­ния http-запро­са на дру­гой веб-сервер:


location / {
proxy_pass $scheme://192.168.0.15:8080/;
proxy_redirect     off;
proxy_set_header   Host             $host;
proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
}

* в дан­ном слу­чае, при­ни­мать запро­сы от бра­у­зе­ра и отве­чать на них будет NGINX, а сама обра­бот­ка будет выпол­нять­ся на сер­ве­ре с IP-адре­сом 192.168.0.15 на пор­ту 8080.

Исполь­зо­ва­ние NGINX в каче­стве http-прокси:

server {

server_name site1.ru www.site1.ru;
location / {
proxy_pass http://192.168.1.21/;
proxy_redirect     off;
proxy_set_header   Host             $host;
proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
}
}

server {

server_name site2.ru www.site2.ru;
location / {
proxy_pass http://192.168.1.22/;
proxy_redirect     off;
proxy_set_header   Host             $host;
proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
}
}

* в дан­ном при­ме­ре запро­сы на site1.ru будут пере­ки­ну­ты на сер­вер 192.168.1.21, а запро­сы на site2.ru — 192.168.1.22.

HTTP proxy с авто­ри­за­ци­ей (если уда­лен­ный веб-сер­вер тре­бу­ет аутентификации):

server {

location / {
proxy_pass http://10.10.10.10/page/;
proxy_set_header Authorization "Basic dGVzdDp0ZXN0";

}
}

* где 10.10.10.10/page — стра­ни­ца, на кото­рую будут пере­ки­ну­ты запро­сы; dGVzdDp0ZXN0 — логин:пароль test:test, зако­ди­ро­ван­ные в фор­ма­те base64.

2. Часть url на другой сервер

Выше мы рас­смот­ре­ли при­мер пере­на­прав­ле­ния запро­са по части веб-адре­са. По схо­же­му сце­на­рию мы можем делать проксирование:

server {

location  ~ ^/page1/(.*)$ {
proxy_pass   $scheme://10.10.10.10/$1;
}
}

* и так, в дан­ном при­ме­ре при обра­ще­нии по адре­су site.ru/page1/<что-то еще>, nginx сде­ла­ет внут­рен­ний запрос на сер­вер 10.10.10.10 по адре­су 10.10.10.10/<что-то еще> и вер­нет гото­вый ответ.

3. На другой сайт

Мы можем сде­лать так, что при пере­хо­де по одно­му адре­су у нас будет откры­вать­ся совер­шен­но дру­гой сайт:

server {

location / {
proxy_pass https://www.test.ru;
proxy_set_header   Host             www.test.ru;
proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
}
}

* в дан­ном слу­чае мы при обра­ще­нии к наше­му сер­ве­ру будем попа­дать на сайт https://www.test.ru. Обра­ти­те вни­ма­ние, что в proxy_set_header мы пере­да­ем хосту его имя — в про­тив­ном слу­чае, как пра­ви­ло, дру­гой сер­вер вер­нет ошиб­ку. Так­же мы не ука­зы­ва­ем proxy_redirect, ина­че, nginx будет пере­во­дить запро­сы на реаль­ный сайт (отправ­лять инструк­ции бра­у­зе­ру перей­ти на него), а не тот, что мы исполь­зу­ем за http-прокси.

Немного о 301 и 302

В чем прин­ци­пи­аль­ная раз­ни­ца меж­ду отве­том с кодом 301 и 302? Для обыч­но­го посе­ти­те­ля сай­та раз­ни­цы нет. А вот для поис­ко­во­го робо­та раз­ни­ца огромная.

301-й реди­рект гово­рит о скле­и­ва­нии стра­ниц. Это озна­ча­ет для поис­ко­ви­ка то, что ста­рая и новая стра­ни­цы — это одно и то же. Таким обра­зом, резуль­та­ты ран­жи­ро­ва­ния необ­хо­ди­мо сохра­нить для новой страницы.

302-о пере­на­прав­ле­ние про­сто гово­рит о том, что нуж­но перей­ти по дру­го­му адре­су. Поис­ко­вый робот не сохра­ня­ет резуль­тат выда­чи для новой стра­ни­цы, индек­си­руя его с нуля.