Debian.pro/

Про Debian


Приводим все урлы на сайте в lowercase (нижний регистр) с помощью nginx

Некоторые поисковики (не будем показывать пальцем в гугл) считают ссылки вида http://site.com/InDex и http://site.com/index разными ссылками. Из-за этого можно натыкать ссылок на InDex на других сайтах, гугл их поиндексирует и найдет дубликат страницы у вас на сайте. (/me почесал затылок). По крайней мере, мне так рассказывали. Меня же во всей этой истории заинтересовало, что эту «проблему» можно пофиксить nginx-ом (интересно, а чего он вообще не умеет?).

Для всего этого ада нам понадобится nginx с perl-модулем (на lua я писать вообще не умею, даже копипастить lua не умею — так что моё решение на перле), точнее собранный с —with-http_perl_module
Я его обычно в пакете nginx-extras отыскиваю.

После установки правильного nginx-а кидаем в файл /etc/nginx/conf.d/99_lowercase.conf такую билиберду:

perl_modules perl/lib;
perl_set $uri_lowercase 'sub {
my $r = shift;
my $uri = $r->uri;
$uri = lc($uri);
return $uri;
}';


perl_set $args_lowercase 'sub {
my $r = shift;
my $args = $r->args;
$args = lc($args);
return $args;
}';

А в конфиг нужного сайта:

if ( $args ~ [A-Z] ) { return 301 $scheme://$http_host$uri?$args_lowercase; }
location ~[A-Z] { return 301 $scheme://$http_host$uri_lowercase?$args; }

Теперь все ссылки вида http://site.com/Admin?admin=Admin будут редиректить на http://site.com/admin?admin=admin (то бишь не конкретно сюда, а на туже ссылку, но в нижнем регистре).


Комментарии (27):

  1. Ок, а как быть с кириллицей? Если к примеру запрашивают http://site.com/Admin?admin=Админ ?

  2. А вот хз, там же урлы когда-то декодятся, когда-то нет.
    Для русских, имхо, проще заменой символа на символ пройти.

    Так-то perl в наши времена вроде utf-compatible (достаточно use utf8; добавить в функции), но вот как завернуть if и location в эту функцию с учетом замены символов сходу непонятно. Попробуйте регулярки вида [А-Я], может заработают.

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

    perl_set $uri_lowercase ‘sub {
    use Encode;
    my $r = shift;
    my $uri = $r->uri;
    $uri = Encode::encode_utf8(lc(Encode::decode_utf8($r->uri)));
    return $uri;
    }’;

    location / {

    if ( $uri != $uri_lowercase ) {
    rewrite ^(.*)$ $scheme://$host$uri_lowercase permanent;
    }

    index index.php;
    }

  4. А еще можно не собирать кому не хочется, а просто поставить эти пакеты.
    apt install nginx-full nginx-extras

  5. «Я его обычно в пакете nginx-extras отыскиваю. «

  6. nginx-full и nginx-extras, кстати, вместе не поставятся, это конфликтующие пакеты.

  7. Действительно конфликтуют. Я ставил nginx-extras и он работает. Спасибо за дополнение!

  8. roman :

    статья полезная, но давняя, хотя может кто и читает-отвечает еще. все это хорошо, если надо перевести все буквы, а если только в самом адресе, а параметры не трогать? например с http://site.com/Admin?admin=Admin на http://site.com/admin?admin=Admin ?

  9. Так там сам кусок конфига состоит из двух частей, используйте только один.

    > location ~[A-Z] { return 301 $scheme://$http_host$uri_lowercase?$args; }
    Этот кусок отвечает только за uri.

    > if ( $args ~ [A-Z] ) { return 301 $scheme://$http_host$uri?$args_lowercase; }
    А этот за аргументы.

  10. (только копируйте из поста, а не из комментария, лень верстать спецсимволы в комментарии)

  11. roman :

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

  12. > а без дополнительного модуля нельзя ли это сделать
    «Для всего этого ада нам понадобится nginx с perl-модулем (на lua я писать вообще не умею, даже копипастить lua не умею — так что моё решение на перле), точнее собранный с —with-http_perl_module
    Я его обычно в пакете nginx-extras отыскиваю. «

  13. roman :

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

  14. roman :

    я бы и в роутере все сделал, но не получается адрес отловить как есть, а выражение не срабатывает.

  15. > не подскажете ли как добавлять конечные слэши при их отсутствии

    rewrite ^/([^?/.]+)(\?.*)?$ /$1/$2 redirect;
    rewrite ^/([^?]+/[^?/.]+)(\?.*)?$ /$1/$2 redirect;

    остальной текст комментария я не распарсил )

  16. roman :

    спасибо. извините, все в кучу слепил. если несколько слэшей подряд сделано. например site.ru/qwe//wwe///rt превратить в site.ru/qwe/wwe/rt/. а если в идеале то site.ru/qwE//wWe///rt превратить в site.ru/qwe/wwe/rt/ при том что есть еще и параметры site.ru/qwe/wwe/rt/?WER=yt&ff=Thy

  17. roman :

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

  18. Несколько слешей убрать можно так:
    if ($request_uri ~ "^[^?]*?//") { rewrite "^" $scheme://$host$uri permanent; }

  19. roman :

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

  20. Я не золотая рыбка =)

  21. roman :

    извините. это был риторический вопрос. премного благодарен за оказанную помощь. я пока пытаюсь это делать на express.router, чтобы не торгать нгинкс.

  22. roman :

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

  23. Да не за чт

  24. anton :

    Подскажите как исключить некоторые location, из редиректа на нижний регистр, к примеру http://somesite.ru/admin/* http://somesite.ru/dealer/* нужно что б в локейшенах редирект не отрабатывал

  25. if ( $args ~ [A-Z] ) { return 301 $scheme://$http_host$uri?$args_lowercase; }
    location ~[A-Z] { return 301 $scheme://$http_host$uri_lowercase?$args; }

    Эти 2 строчки только в нужный location прописывайте.
    Nginx поддерживает вложенные location.

  26. anton :

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

  27. В плане?
    для location / прописываете lowercase, для исключенных локаций отдельные секции без этих строк.

Написать комментарий