Интересная проблема в .htaccess или спецсимволы, mod_rewrite и тег C++.

    Недавно, при работе над своим проектом (сайт со статьями на тему «как сделать»), столкнулся с проблемой в работе mod_rewrite. Суть проблемы заключалась в следующем: в облаке тегов, при переходе на тег «C++» (обработанный urlencode и ставший C%2B%2B) я попадал на тег «С » (буква «С» и 2 пробела).

    Правило в .htaccess было таким:

    RewriteRule ^tag/([^/]+)/$ index.php?tag=$1 [L]

    Путем экспериментов выяснил, что в $1 правила попадает не «C%2B%2B», а «C++» (внутри апача %2B превращается в +). Плюс, естественно, расценивался как пробел, соединяющий два слова.

    Долго копал интернет в поисках решения, экспериментировал с флагами, пока по запросу в гугле «C++ mod_rewrite» не нашел пост с описанием данного бага апача где-то на просторах форумов на apache.org (насколько я понял из этого поста — моя проблема это баг апача).

    В этом посте автор советовал разбирать регулярками %{THE_REQUEST}, в котором «C%2B%2B» был в исходной форме, а не заменялся на «C++». В %{THE_REQUEST} обычно содержится что-то наподобие «GET tag/C%2B%2B/ HTTP 1.1».

    Прием помог, я переделал старое рерайт-правило на такое:

    RewriteCond %{THE_REQUEST} ^GET[\ ]+/tag/([^/]+)/[\ ]+HTTP.*$
    RewriteRule ^(.*)$ index.php?tag=%1 [L]

    Проверено на версиях апача 2.2 (винда) и 1.3.4 (линукс).

    Теперь пользователи моего сайта могут без проблем читать статьи с тегом «C++» :)
    Надеюсь, мой пост поможет кому-нибудь в аналогичной проблеме.
    Поделиться публикацией

    Похожие публикации

    Комментарии 15

      –2
      Спасибо, возьму на заметку)
        +2
        А мне кажется, что некорректно использовать знак + в УРЛ.
        Если сайт сами делаете, то лучше следить за этим и писать cpp
        А если контент вам юзеры заливают, лучше заменять + на plus по правилу:
        Если строка содержит только + то plus
        Если строка начинается с + то plus_
        Если строка кончается + то _plus
        Если + не в конце и не в начале, то _plus_, при этом убирая двойные _.

        Вобщем думать надо как составить.

        Мне кажется что нижеследующие ссылки выглядят достаточно красиво:
        /langs/cpp/history
        /cpp/lising
        /blogs/c_plus_plus/about
        /symbol/plus/info
        /systems/plus_data/about
          0
          Как вариант — cxx.
            +3
            А также варианты «C‡» и «C††»

            . оскорбить никого не хотел.
          0
          Попробуйте
          RewriteRule ^tag/([^/]+)/$ index.php?tag=%{escape:$1} [L]
            0
            Могут быть проблемы. Ведь в случае с С++ плюс это символ, а в случае два+слова плюс это пробел
              0
              пробел — это «два%20слова»
            +2
            Эта проблема не баг апача, просто и mod_rewrite делает urldecode(), превращающий %20 в пробел, а %2B в +, после чего передает уже пропарсированный урл в php (в $_SERVER['QUERY_STRING']). PHP в свою очередь при разборе query_string и заполнении $_GET каждому значению делает еще один urldecode(), который превращает + в пробел. Для твоего частного случая когда необходимо получить только тег, подойдет и то правило, но что б каждый раз не добавлять/изменять правил и что б плюсы в каждом элементе $_GET оставались плюсами, можно разбирать query_string самостоятельно. Приблизительно вот так:
            function _GET() {
            	$pairs = explode('&', $_SERVER['QUERY_STRING']);
            	
            	$get = array();
            	
            	foreach ($pairs as $pair) {
            		list($key, $value) = split('=', $pair);
            		
            		$get[$key] = $value;
            	}
            	
            	return $get;
            }
            
            $_GET = _GET();
            


            Ну и конечно при формировании ссылки на сайте пробел необходимо заменять не на "+", а на %20, а плюс в свою очередь на %2B.
              0
              А как на счет site/x=a&b
                0
                то есть site/a&b/
                  0
                  да что ж такое) Короче с амперсандом глюк останется
                    +1
                    это не глюк, амперсанд не имеет отношения к urlencode(), это спец-символ xml/html, который браузер при запросе к серверу преобразовывает в символ, который им заменяется (в данном случае "&"). единственная проблема которая может возникнуть — не все поисковые пауки преобразовывают "&амп;" в "&" и из-за этого ломается вся индексация. приведенный выше код борется не с этим.
                      0
                      я говорю о том, что при href=«urlencode('a&b')»
                      и RewriteRule ^/([^/]+)/$ index.php?tag=$1 [L]

                      tag будет равен 'a' а не 'a&b'
                        0
                        и ваша function _GET() не спасает от этого
              0
              А какая основная нагрузка этого поста — "+" в УРЛе, или УРЛ на свой сайт в посте?

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое