Recursos ocultos do mod_rewrite

119

Parece haver um número decente de mod_rewritethreads flutuando ultimamente com um pouco de confusão sobre como certos aspectos dele funcionam. Como resultado, compilei algumas notas sobre funcionalidades comuns e talvez algumas nuances irritantes.

Quais outros recursos / problemas comuns você já encontrou mod_rewrite?

Owen
fonte
5
Veja também serverfault.com/questions/214512/…
Michael Myers

Respostas:

203

Onde colocar regras mod_rewrite

mod_rewriteregras podem ser colocadas dentro do httpd.confarquivo ou dentro dele .htaccess. se você tiver acesso httpd.conf, a colocação de regras aqui oferecerá um benefício de desempenho (como as regras são processadas uma vez, ao contrário de cada vez que o .htaccessarquivo é chamado).

Registrando solicitações mod_rewrite

O log pode ser ativado de dentro do httpd.confarquivo (incluindo <Virtual Host>):

# logs can't be enabled from .htaccess
# loglevel > 2 is really spammy!
RewriteLog /path/to/rewrite.log
RewriteLogLevel 2

Casos de uso comuns

  1. Para canalizar todas as solicitações para um único ponto:

    RewriteEngine on
    # ignore existing files
    RewriteCond %{REQUEST_FILENAME} !-f   
    # ignore existing directories
    RewriteCond %{REQUEST_FILENAME} !-d   
    # map requests to index.php and append as a query string
    RewriteRule ^(.*)$ index.php?query=$1 
    

    Desde o Apache 2.2.16, você também pode usar FallbackResource.

  2. Manipulando redirecionamentos 301/302:

    RewriteEngine on
    # 302 Temporary Redirect (302 is the default, but can be specified for clarity)
    RewriteRule ^oldpage\.html$ /newpage.html [R=302]  
    # 301 Permanent Redirect
    RewriteRule ^oldpage2\.html$ /newpage.html [R=301] 
    

    Nota : redirecionamentos externos são implicitamente redirecionamentos 302:

    # this rule:
    RewriteRule ^somepage\.html$ http://google.com
    # is equivalent to:
    RewriteRule ^somepage\.html$ http://google.com [R]
    # and:
    RewriteRule ^somepage\.html$ http://google.com [R=302]
    
  3. Forçando SSL

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://example.com/$1 [R,L]
    
  4. Sinalizadores comuns:

    • [R]ou [redirect]- forçar um redirecionamento (o padrão é um redirecionamento 302 temporário)
    • [R=301]ou [redirect=301]- forçar um redirecionamento permanente 301
    • [L]ou [last]- interrompa o processo de reescrita (veja a nota abaixo em armadilhas comuns)
    • [NC]ou [nocase]- especificar que a correspondência não diferencia maiúsculas de minúsculas


    O uso da forma longa de sinalizadores geralmente é mais legível e ajudará outras pessoas que vierem a ler seu código posteriormente.

    Você pode separar vários sinalizadores com uma vírgula:

    RewriteRule ^olddir(.*)$ /newdir$1 [L,NC]
    

Armadilhas comuns

  1. Misturando mod_aliasredirecionamentos de estilo commod_rewrite

    # Bad
    Redirect 302 /somepage.html http://example.com/otherpage.html
    RewriteEngine on
    RewriteRule ^(.*)$ index.php?query=$1
    
    # Good (use mod_rewrite for both)
    RewriteEngine on
    # 302 redirect and stop processing
    RewriteRule ^somepage.html$ /otherpage.html [R=302,L] 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # handle other redirects
    RewriteRule ^(.*)$ index.php?query=$1                 
    

    Nota : você pode misturar mod_aliascom mod_rewrite, mas envolve mais trabalho do que apenas lidar com redirecionamentos básicas como acima.

  2. O contexto afeta a sintaxe

    Nos .htaccessarquivos, uma barra inicial não é usada no padrão RewriteRule:

    # given: GET /directory/file.html
    
    # .htaccess
    # result: /newdirectory/file.html
    RewriteRule ^directory(.*)$ /newdirectory$1
    
    # .htaccess
    # result: no match!
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # httpd.conf
    # result: /newdirectory/file.html
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # Putting a "?" after the slash will allow it to work in both contexts:
    RewriteRule ^/?directory(.*)$ /newdirectory$1
    
  3. [L] não é o último! (as vezes)

    O [L]sinalizador interrompe o processamento de quaisquer regras de reescrita adicionais para essa passagem pelo conjunto de regras . No entanto, se o URL foi modificado nessa passagem e você estiver no .htaccesscontexto ou na <Directory>seção, sua solicitação modificada será transmitida novamente pelo mecanismo de análise de URL novamente. E no próximo passe, pode corresponder a uma regra diferente dessa vez. Se você não entende isso, muitas vezes parece que sua [L]bandeira não teve efeito.

    # processing does not stop here
    RewriteRule ^dirA$ /dirB [L] 
    # /dirC will be the final result
    RewriteRule ^dirB$ /dirC     
    

    Nosso log de reescrita mostra que as regras são executadas duas vezes e a URL é atualizada duas vezes:

    rewrite 'dirA' -> '/dirB'
    internal redirect with /dirB [INTERNAL REDIRECT]
    rewrite 'dirB' -> '/dirC'
    

    A melhor maneira de contornar isso é usar o [END]sinalizador ( consulte a documentação do Apache ) em vez do [L]sinalizador, se você realmente deseja interromper todo o processamento adicional de regras (e passes subsequentes). No entanto, a [END]sinalização está disponível apenas para o Apache v2.3.9 + ; portanto, se você tiver a v2.2 ou inferior, ficará com apenas a [L]sinalização.

    Para versões anteriores, você deve confiar nas RewriteCondinstruções para impedir a correspondência de regras nas passagens subsequentes do mecanismo de análise de URL.

    # Only process the following RewriteRule if on the first pass
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ...
    

    Ou você deve garantir que suas RewriteRule estejam em um contexto (ou seja httpd.conf) que não fará com que sua solicitação seja analisada novamente.

Owen
fonte
10
Cara, totalmente o melhor artigo da internet agora sobre reescrita de mod. Eu odeio essa coisa. Eu sou um herege lighttpd por causa de quanto eu odeio mod_rewrite.
Kent Fredric
3
Este foi o guia mais útil que encontrei no mod_rewrite até agora. Descobrir o RewriteLog ajudou a corrigir tantos problemas que o que estava me levando dias para rastrear se transformou em alguns minutos. (Quero dizer, as regras foram escritas, mas eu não conseguia descobrir por que eles não estavam trabalhando)
Joe Chin
Postagem de 1 ano, mas uma das coisas mais úteis que encontrei no SO - para mim.
Erik
3
O [L]sinalizador significa que uma regra é a última no processamento atual; isso não interrompe a reescrita, porque são redirecionamentos internos; portanto, você dirBaplica-se ao dirCpróximo processamento htaccess. Sozinho, RewriteRule ^(.*)$ index.php?query=$1haverá um loop infinito de redirecionamentos internos (na prática, é encerrado após 10 iterações). -1 porque você sugere que [L] não é o último . Não está encerrando o processo de reescrita, mas é o último .
Kbec 15/05
3
Eu acredito que RewriteCond %{HTTPS} offé a forma preferida para verificar se há uma conexão HTTPS (no seu exemplo de forçar o tráfego não-SSL para HTTPS)
Madbreaks
22

se você precisar "bloquear" redirecionamentos / reescritos internos no arquivo .htaccess, consulte o

RewriteCond %{ENV:REDIRECT_STATUS} ^$

condição, conforme discutido aqui .

mromaina
fonte
Obrigado, isso acabou de resolver o meu problema!
Mateus
Obrigado por mim também, salva-vidas!
Benjamin
Este é realmente um salva-vidas! As pessoas devem estar mais conscientes disso. Na verdade, vou sugerir isso para todas as perguntas sobre .*a [L]bandeira que li antes de chegar aqui.
Qwerty
Eu vi várias modificações a esta 200, !=200, ^., ^$. Aparentemente, a variável é definida como 200redirecionada, mas também outras páginas (erro e outras coisas) definem-na para algum valor. Agora isso significa que você quer verificar se ele is empty, is not empty, is 200ou is not 200, dependendo do que você precisa.
Qwerty 25/05
18

O acordo com RewriteBase:

Você quase sempre precisa definir o RewriteBase. Caso contrário, o apache acha que sua base é o caminho do disco físico para o seu diretório. Então comece com isso:

RewriteBase /
Sean McMillan
fonte
Ah Isso resolveu totalmente o problema que eu estava tendo. Obrigado por isso!
Tom Savage
3
Alguma maneira de dizer RewriteBase .ou algo para indicar que ele deve manter o URL igual, apenas alterando o que você especificou?
Jay K
Obrigado, esta é uma informação inestimável. :)
AturSams 15/10
2
Você só precisa definir RewriteBasese estiver usando a substituição de caminho relativo na RewriteRulediretiva. É melhor evitar o uso de caminhos relativos.
MrWhite
2
Eu discordo desta resposta. Em nossa equipe de desenvolvimento, evitamos RewriteBasecompletamente, pois quase todos os desenvolvedores não entendem o que fazem. Como o @ w3d disse, você só precisa se deseja salvar caracteres e aplicar a mesma base a todas as suas RewriteRules em um arquivo. Seu código provavelmente será mais claro para os outros se você evitá-lo.
Simon East
13

Outras armadilhas:

1- Às vezes, é uma boa ideia desabilitar o MultiViews

Options -MultiViews

Não sou versado em todos os recursos do MultiViews, mas sei que ele atrapalha minhas regras mod_rewrite quando ativo, porque uma de suas propriedades é tentar 'adivinhar' uma extensão de um arquivo que ele procura. .

Vou explicar: suponha que você tenha 2 arquivos php em seu diretório da web, file1.php e file2.php e adicione essas condições e regra ao seu .htaccess:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ file1.php/$1 

Você assume que todos os URLs que não correspondem a um arquivo ou diretório serão capturados por file1.php. Surpresa! Esta regra não está sendo respeitada pelo URL http: // myhost / file2 / somepath . Em vez disso, você é levado para dentro do arquivo2.php.

O que está acontecendo é que o MultiViews adivinhou automaticamente que o URL que você realmente queria era http: //myhost/file2.php/somepath e com prazer o levou até lá.

Agora, você não tem idéia do que aconteceu e, nesse momento, está questionando tudo o que achou que sabia sobre o mod_rewrite. Você começa a brincar com as regras para tentar entender a lógica por trás dessa nova situação, mas quanto mais testando, menos sentido faz.

Ok, resumindo, se você deseja que o mod_rewrite funcione de maneira que se aproxime da lógica, desativar o MultiViews é um passo na direção certa.

2- ativar o FollowSymlinks

Options +FollowSymLinks 

Na verdade, eu não conheço os detalhes, mas já o vi mencionado muitas vezes, então faça isso.

Michael Ekoka
fonte
Obrigado :) Notei surpresas inesperadas como / log / activity transformando em /log.txt/activity .. Obrigado pela dica :) .. computadores ruins nunca tiram sarro coisas inesperadas acontecem como seduzir acidentalmente todas as suas colegas de trabalho no facebook :)
AturSams
1
+FollowSymLinksé mencionado na documentação como obrigatório para o mod_rewritetrabalho, por vagas razões de segurança.
Joey
Duas declarações aqui me preocupam imensamente: 'Eu não sou muito versado em todos os recursos do MultiViews, mas sei que isso atrapalha minhas regras de mod_rewrite quando ativo' e essa 'Esse, eu realmente não sei os detalhes de , mas já o vi mencionado várias vezes, então faça-o. ' Eu gostaria que pessoas como você não escrevessem respostas sobre o SO sobre coisas que você não tem certeza.
TheCarver
1
@PaparazzoKid: Acho que você está confundindo SO com uma enciclopédia. É uma comunidade de pessoas se unindo para entender melhor a tecnologia com a qual estão trabalhando. Ao contrário de AW White e Joey antes de você, seu comentário é quase nulo de valor. MV e FSL são duas das muitas opções do Apache. Minha resposta é sobre armadilhas ao trabalhar especificamente com o mod_rw, um módulo separado, que entra em conflito com algumas opções e funciona com outras. Expliquei como o MV afeta o mod_rw e mencionei que o + FSL é uma recomendação popular. Joey confirmou que é de fato obrigatório. O que você traz para a mesa?
Michael Ekoka
Obrigado. Passei quase uma hora trabalhando com um site legado e tentando depurar as regras de reescrita, apenas para descobrir que o MultiViews estava substituindo tudo.
Andrew McCombe
5

A equação pode ser feita com o seguinte exemplo:

RewriteCond %{REQUEST_URI} ^/(server0|server1).*$ [NC]
# %1 is the string that was found above
# %1<>%{HTTP_COOKIE} concatenates first macht with mod_rewrite variable -> "test0<>foo=bar;"
#RewriteCond search for a (.*) in the second part -> \1 is a reference to (.*)
# <> is used as an string separator/indicator, can be replaced by any other character
RewriteCond %1<>%{HTTP_COOKIE} !^(.*)<>.*stickysession=\1.*$ [NC]
RewriteRule ^(.*)$ https://notmatch.domain.com/ [R=301,L]

Balanceamento de carga dinâmico:

Se você usar o mod_proxy para equilibrar seu sistema, é possível adicionar um intervalo dinâmico de servidor de trabalho.

RewriteCond %{HTTP_COOKIE} ^.*stickysession=route\.server([0-9]{1,2}).*$ [NC]
RewriteRule (.*) https://worker%1.internal.com/$1 [P,L]
DrDol
fonte
4

Uma melhor compreensão da bandeira [L] está em ordem. O sinalizador [L] é o último, você apenas precisa entender o que fará com que sua solicitação seja roteada pelo mecanismo de análise de URL novamente. Dos documentos ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (ênfase minha):

O sinalizador [L] faz com que mod_rewrite pare de processar o conjunto de regras. Na maioria dos contextos, isso significa que, se a regra corresponder, nenhuma regra adicional será processada. Isso corresponde ao último comando em Perl ou ao comando break em C. Use esse sinalizador para indicar que a regra atual deve ser aplicada imediatamente sem considerar outras regras.

Se você estiver usando o RewriteRule nos arquivos .htaccess ou nas <Directory>seções , é importante entender como as regras são processadas. A forma simplificada disso é que, uma vez processadas as regras, a solicitação reescrita é devolvida ao mecanismo de análise de URL para fazer o que for possível. É possível que, à medida que a solicitação reescrita seja manipulada, o arquivo ou a<Directory> seção.htaccesspossa ser encontrado novamente e, portanto, o conjunto de regras possa ser executado novamente desde o início. Geralmente isso acontece se uma das regras causar um redirecionamento - interno ou externo - fazendo com que o processo de solicitação seja reiniciado.

Assim, o [L] bandeira faz parar o processamento de quaisquer regras adicionais de reescrita para que passe através do conjunto de regras. No entanto, se sua regra marcada com [L] modificou a solicitação e você está no contexto .htaccess ou na <Directory>seção, sua solicitação modificada será passada de volta pelo mecanismo de análise de URL novamente. E no próximo passe, pode corresponder a uma regra diferente dessa vez. Se você não entender o que aconteceu, parece que sua primeira regra de reescrita com o sinalizador [L] não teve efeito.

A melhor maneira de contornar isso é usar o sinalizador [END] ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) em vez do sinalizador [L], se você realmente deseja parar todo o processamento adicional de regras (e subsequente nova correção). No entanto, o sinalizador [END] está disponível apenas para o Apache v2.3.9 +; portanto, se você tiver o v2.2 ou inferior, ficará com apenas o sinalizador [L]. Nesse caso, você deve confiar nas instruções RewriteCond para impedir a correspondência de regras nas passagens subsequentes do mecanismo de análise de URL. Ou você deve garantir que suas RewriteRule estejam em um contexto (por exemplo, httpd.conf) que não fará com que sua solicitação seja analisada novamente.

JaredC
fonte
3

Outro ótimo recurso são reescrever-mapa-expansões. Eles são especialmente úteis se você tiver uma grande quantidade de hosts / reescritas para lidar com:

Eles são como uma substituição de valor-chave:

RewriteMap examplemap txt:/path/to/file/map.txt

Então você pode usar um mapeamento em suas regras como:

RewriteRule ^/ex/(.*) ${examplemap:$1}

Mais informações sobre este tópico podem ser encontradas aqui:

http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc

ESTAR
fonte
Ignore esse recurso se você estiver usando .htaccessreescritas com base. Não funciona neste contexto.
TerryE
2
A diretiva RewriteMap deve ser usada no contexto do servidor (httpd.conf), mas uma vez definida lá, você pode usar o mapa por meio do RewriteRule em um arquivo .htaccess.
JaredC
2

mod_rewrite pode modificar aspectos do tratamento de solicitações sem alterar a URL, por exemplo, definindo variáveis ​​de ambiente, definindo cookies, etc. Isso é incrivelmente útil.

Defina condicionalmente uma variável de ambiente:

RewriteCond %{HTTP_COOKIE} myCookie=(a|b) [NC]
RewriteRule .* - [E=MY_ENV_VAR:%b]

Retornar uma resposta 503: RewriteRuleo [R]sinalizador pode pegar um valor que não seja 3xx e retornar uma resposta que não redireciona, por exemplo, para tempo de inatividade / manutenção gerenciados:

RewriteRule .* - [R=503,L]

retornará uma resposta 503 (não um redirecionamento em si).

Além disso, mod_rewrite pode atuar como uma interface superpoderosa para mod_proxy, para que você possa fazer isso em vez de escrever ProxyPassdiretivas:

RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]

Opinião: Usar RewriteRules e RewriteConds para rotear solicitações para diferentes aplicativos ou balanceadores de carga com base em praticamente qualquer aspecto concebível da solicitação é imensamente poderoso. Controlar solicitações no caminho para o back-end e poder modificar as respostas no caminho de volta faz do mod_rewrite o local ideal para centralizar toda a configuração relacionada ao roteamento.

Aproveite o tempo para aprender, vale a pena! :)

semana
fonte