Полезные редиректы в nginx

В данной статье буду собирать все редиректы в nginx которыми пользуюсь

Рассматриваю на примере конфигурации веб-окружения BitrixVM, т.к. занимаюсь разработкой именно в данной среде, но правила одинаковые для любых систем, поэтому, это не принципиально.

Правила нужно прописывать в файле с описаниемм конфигурации конкретного хоста (для BitrixVM это
/etc/nginx/bx/site_avaliable/bx_ext_domain.conf - для сайта, работающего без ssl;
/etc/nginx/bx/site_avaliable/bx_ext_ssl_domain.conf - при работе по ssl);

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

Первый:

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

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

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

Второй:

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

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

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

Редирект с http: на https

if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }

Или, если вам повезло также как и мне и конфигурационные файлы для ssl и без ssl - разные, то в файле для без ssl можно прописать прямой редирект:

server {
    #...
    listen  server_ip:80;
    server_name  www.sitename.com;
    rewrite ^ https://www.sitename.com$request_uri? permanent;
}

редирект с www на без www

if ($host ~* www\.(.*)) {
        set $host_without_www $1;
        rewrite ^(.*)$ http://$host_without_www$1 permanent;
    }

Добавляем слеши в конце

if (!-f $request_filename) {
     rewrite [^/]$ $uri/ permanent;
   }

Убираем слеш в конце

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

Убираем index.php в конце адреса

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

Если для определенных разделов данный редирект нужно отключить, то в маску добавляем разделы исключения:

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

Чтобы сохранить параметры в адресной строке, нужно задать:

if ($request_uri ~ "^(.*)index\.(?:php|html)") {
      return 301 $1$is_args$args; break; # break нужен в том случае,
					#если происходит склейка нескольких редиректов одновременно
}

Редирект для конкретной страницы

Обычный редирект в htaccess имеет вид:

Redirect 301 /catalog/section_1/section_1_1/ /catalog/section_1_1/

В nginx примет вид:

rewrite /catalog/section_1/section_1_1/ /catalog/section_1_1/ permanent;

Также, редирект конкретного файла можно сделать так:

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

Чтобы удалить из адреса часть строки, можно сделать так:

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

или так:

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

Редирект с одного домена на другой

server {
    server_name domain.com www.domain.com;
    rewrite ^ $scheme://www.new-domain.com;
}

Редирект с каждой страницы одного домена на такой же URL адрес другого домена

server {
    server_name domain.com www.domain.com;
    rewrite ^ $scheme://www.new-domain.com$request_uri permanent;
}

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

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

Избавляемся от задвоенных слешей в адресе

По умолчанию в  nginx включено воспринимать задвоенные слеши, как один. Для начала нужно отключить данное поведение. Для этого в конфигурационном файле конкретного сайта пишем в начале секции server (после директивы server_name) отключение данного поведения:

 merge_slashes off;

Далее в блоке с редиректами прописываем:

location ~* .*//+.* {
        rewrite (.*)//+(.*) $1/$2 permanent;
}

Сложный анализ ЧПУ

Пример из сети по обработке api-запросов:

location ~* api/ {
        rewrite ^/api/(.*)$ /api.php?_d=$1&ajax_custom=1&$args last;
}

Пока не приходилось сталкиваться, но, может быть, кому-то будет полезным!

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

Для этого, в основном конфигурационном файле (для bitrixVM это /etc/nginx/bx/site_available/s1.conf) пишем:

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, необходимо указывать в настройках пути к сертификатам.

Редирект на особенную страницу по 404-му статусу

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

location ~ \.php$ {
	....
	try_files = $uri @missing;
}
location @missing {
	rewrite ^ $scheme://$host/some_file.php permanent;
}

По этой логике можно определять любые конструкции, которые вам нужны. Сначала сервер попробует выполнить файл, и, если он не будет найден, он переместится в часть @missing. А второй блок, с @missing, перенаправит на нужную страницу.

Использование вложенных if

Вложенные if в конфигурации nginx запрещены, поскольку это вовсе не языковая конструкция nginx, а обычная директива из модуля rewrite, использование которой в ее же собственном контексте if не предусмотрено. Использование списка условий, разделенных логическими операторами "и" и "или" тоже не предусмотрено. Обычно для эмуляции вложенных условий с использованием директивы if предлагают использовать регулярные выражения. Например, проверить, что имя, указаное в HTTP хедере HOST, соответствует значению localhost, а в URI присутствует аргумент "a" (в этом случае переменная $arg_a будет не пустой), можно так:

location /test_if.html {
	set $cond $host::$arg_a;
	if ($cond ~* '^localhost::.+') {
		echo "Matched: host = '$host', a = '$arg_a'";
		break;
	}
	echo "Not matched: host = '$host', a = '$arg_a'";
}

К сожалению, условие в директиве if имеет ограничения. По крайней мере, это не выражение, и оно не может быть составлено в виде цепочки выражений, разделенных логическими операторами. Кроме того, в случае сравнения чего-то с регулярным выражением, это что-то может быть только именем переменной, именно поэтому пришлось ввести новую переменную $cond:

location /test_if.html {
	set $goodhost 'localhost';
	set $cond $host::$goodhost::$arg_a;
	if ($cond ~* '^(.*)::\1::.+') {
		echo "Matched: host = '$host', a = '$arg_a'";
		break;
	}
	echo "Not matched: host = '$host', a = '$arg_a'";
}

В данном примере мы соединили через произвольно выбранный нами разделитель :: три переменных $host, $goodhost и $arg_a и присвоили это значение переменной $cond. А регулярное выражение, с которым мы сопоставляем это значение, проверяет, что его первая часть (до разделителя ::) и вторая часть (до второго разделителя ::) равны, а последняя часть (после второго разделителя) не пуста.

В полном виде конструкция проверки примет вид:

location /test_if.html {
	set $goodhost 'localhost';
	set $cond $host::$goodhost::$arg_a;
	if ($cond ~* '^(.*)::\1::ok$') {
		echo "Matched: host = '$host', a is Ok";
		break;
	}
	if ($cond ~* '^(.*)::\1::.+$') {
		echo "Matched: host = '$host', a = '$arg_a'";
		break;
	}
	echo "Not matched: host = '$host', a = '$arg_a'";
}

Описание структуры работы с вложенными if взято отсюда.

Сложное условие и проксирование запросов

Если нужно выполнить проксирование запросов с помозью директивы proxy_pass только при соблюдении нескольких условий, то можно воспользоваться следующей конструкцией:

if ($request_uri = /) {
      set $test  A;
    }

    if ($host ~* domain.com) {
      set $test  "${test}B";
    }

    if ($http_cookie !~* "auth_token") {
      set $test  "${test}C";
    }

    if ($test = ABC) {
      proxy_pass http://domain-new.com;
      break;
}

Пояснения:

Условие RewriteCond обозначает совпадение с которым будет выполнено правило RewriteRule. При этом, используются символы:
. – Точка — это любой символ (но только один!).
^ — Эта метка означает начала строки.
$ — Эта метка означает конец строки.
\ — Эта экранирующий слэш, позволяет считать следующий за ним символ, обычным символом.
() – Этот символ обозначает группировку.
! – Метка отрицания.
+ — Этот символ повторяется от 1 до 65536 раз.
? — Этот символ повторяется 0 или 1 раз.
* — А этот символ повторяется от 0 до 65536 раз.
Флаги определяют дополнительные опции.
R — (redirect) — По умолчанию останавливает процесс преобразования, возвращает результат в браузер клиента, как редирект на данную страницу 302, MOVED TEMPORARY. Например флагу можно указать другой код результата, R=301 и он возвратит редирект клиенту с кодом 301 MOVED PERMANENTLY.
NC — (nocase) — Этот флаг отключает проверку регистра символов.
L — (last) — Флаг останавливает процесс преобразования, текущая ссылка считается окончательной.

Чтобы изменения вступили в силу - не забудьде произвести рестарт nginx. Но для начала - проверьте, что ваша конфигурация в порядке. Для этого выполните команду:

nginx -t

Если выдаст "OK" - делаем смело перезагрузку:

service nginx restart

или

systemctl restart nginx

Первый вариант - для устаревших систем Linux или FreeBSD. Второй - для новых систем Linux

Если же показывает ошибку - разбираемся с ней.

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

Отличия между 301 и 302 редиректами:

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

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

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

P.S. Не забывайте, что внесение любых изменений в конфигурации сервера могут привести к тому, что самостоятельно вы можете и не восстановить. Поэтому не забывайте делать бекапы!

Добавляем слеш и убираем index.php в конце адреса страницы для битрикса

При настройке редиректов для Bitrix есть такая проблема - часть адресов направлены на ядро и настроены на ссылки с index.php. Настройка редиректов ведет к тому, что перестают выполняться некоторые аякс-запросы. Чтобы этого избежать - исключаем ядро битрикса из редиректов.

Вначале (перед server) конфигурационного файла сайта (/etc/nginx/bx/site_available/bx_ext_ssl_sitename.conf) пишем:

map $uri $isNotBx {
        "~^/bitrix/*" 0;
        "~^/local/*" 0;
        "~^/auth/*" 0;
        default 1;
}

Далее после подключения сертификатов пишем:

if ($isNotBx) {
        set $test A;
}

if (!-f $request_filename) {
        set $test  "${test}B";
}

if ($request_uri ~ "^(.*)index\.(?:php|html)") {
        set $test "${test}C";
        set $redirectTo $1;
}

if ($test = ABC ) {
        return 301 $redirectTo;
}

if ( $test = AC ) {
        return 301 $redirectTo;
}

if ($request_uri ~ "(.*).(?:php|html)" ) {
        set $test "${test}D";
}

if ($test = AB ) {
        rewrite [^/]$ $uri/ permanent;
}

Готово!

Количество показов: 38595
06.09.2018

Возврат к списку

Если вам была полезна статья можете отблагодарить автора:
Ethereum:

0x16Df809287333C49D3A237296C6248A6c08702Bc

Разработка сайта

Подайте заявку на разработку сайта на базе готового решения от компании 1С-Битрикс или одного из партнеров компании. Максимально подробно опишите, чему будет посвящен сайт, если это интернет-магазин - что он будет продавать, нужна ли мультиязычность, будут ли разные типы цен (розница, опт, крупный опт), будет ли интеграция с 1С, будет ли выгрузка товаров на различные торговые площадки...

Сопровождение сайта

Вы можете подать заявку на сопровождение вашего сайта на базе 1С-Битрикс. Сопровождение включает в себя: проверка актуальности обновлений сайта, проверка актуальности резервной копии, консультации по сайту. Опишите в заявке, какие еще объемы планируются на сопровождении и на какой срок вы планируете заключить договор на сопровождение - мы подберем подходящий вам бюджет на сопровождение

Работы по сайту

Вы можете подать заявку на выполнение определенного объема работ по сайту. Опишите в заявке объем работ. Это может быть разработка какого-то нового функционала, доработки по имеющемуся функционалу, доработки под требования сео-специалистов. На основании заявки вам будет сформирован бюджет работ, а также названы сроки на выполнение тех или иных работ.