Apache – tutorial mod_rewrite

Introdução

Conforme descrito nessa referência:
“a rule-based rewriting engine to rewrite requested URLs on the fly.”

O mod_rewrite é complexo, faz um uso sem fronteiras de regex e complica a vida de muito administrador; a minha, por exemplo.

Não sou nem especialista nem usuário intermediário. Na verdade estou coletando as informações para esse post conforme estou estudando esse módulo do Apache. Como mais uma vez o que preciso está distribuido em muitos lugares diferentes, resolvi fazer uma coletânea e descrever ao meu ponto de vista (com algumas cópias de texto, com as referências devidamente citadas) diversas regras.

No http.conf deve ter uma linha referenciando o módulo. Se não tiver ou se estiver comentada (com uma cerquilha na frente) então faça com que fique assim:

LoadModule rewrite_module modules/mod_rewrite.so

Reinicie o apache (em Debian nesse exemplo):

/etc/init.d/apache2 restart

Existem duas maneiras possíveis para utilização do módulo em questão. Uma bastante utilizada é escrevendo no .htaccess. Na referência que peguei (no primeiro link desse post) as primeiras linhas a escrever no arquivo são (assim o fiz no virtualhost default):


<IfModule mod_rewrite.c>
Options +FollowSymLinks
Options +Indexes
RewriteEngine On
</IfModule>

A primeira, por questão de segurança é obrigatória; mas foge ao meu entendimento do porque ter que escrevê-la uma vez que é obrigatória.
A segunda linha é para o caso de utilização de indexes.
A terceira fala por sí só.

Para configurar a URL base do seu server, utiliza-se algo assim:

RewriteBase /

A aplicação se tornará mais clara, então não se preocupe com a introdução até esse momento.

A utilização mais básica do rewrite tem o formato:

RewriteRule PATTERN DESTINATION

Por exemplo, http://algumacoisa.com/ contendo a seguinte regra:

RewriteRule /alvoSolicitado.html /arquivoDeDestino.html

Isso redirecionará a requisição do arquivo alvoSolicitado.html para arquivoDeDestino.html. Básico e pouco útil por enquanto, não? Mas vai complicar, não se preocupe.

Expressões regulares
Redirecionar uma página x para uma página y não é tão útil ou aplicável no dia a dia. Na verdade as configurações tendem a um conjunto de regras interpretadas pelo módulo. Essas regras são compostas por passos que em diversas de suas linhas se aplicam expressões regulares.

Se você já está familiarizado com expressões regulares, siga a leitura daqui, senão, nos vemos outro dia.

Supondo um arquivo com php que receba um GET na variável dia de uma agenda:

http://algumacoisa.com/agenda.php?dia=9

Obviamente para o usuário não é simples lembrar uma URL assim. Mas também por questões de estética e até por questões de segurança o formato ideal seria:


http://algumacoisa.com/agenda/9

Agora aquela regra básica fará mais sentido:

RewriteRule ^/agenda/9$ /agenda.php?dia=9

A expressão regular aplicada foi a mais simples possível. O circunflexo indica “que começa” e o cifrão indica “que termina”, ou seja, quando for exatamente http://algumacoisa.com/agenda/9 ele deve ser redirecionado para http://algumacoisa.com/agenda.php?dia=9.

Então, para fazer a agenda do mês, basta fazer 31 linhas como esta. Certo? – claro que não.
As expressões regulares são assustadoras mas deliciosamente maleáveis. Para fazer a agenda do mês (sem muito requinte, pois isso aqui é um exemplo de uso, nada mais) a regra ficaria assim:

RewriteRule ^/agenda/([0-3]+[0-9])$ /agenda.php?dia=$1

Com essa regra estamos preparados para um mês com até 39 dias! Ok, não ficou perfeito, mas serve para o exemplo de uso.
A regra diz que tudo que começar com agenda/1,2,3 e seguir de 0 até 9, deve-se redirecionar para agenda.php para o dia $1. O $1 quer dizer a primeira referência da expressão, que seria justamente o dia enviado na requisição. Se houvessem outros parãmetros, então a expressão se extenderia e sua referência também seria posicional no redirecionamento, aplicando $2, $3, etc.

Uma outra regra muito interessante é a expansão de palavras na requisição. Como pode-se notar, passamos o dia mas não temos referência de mês. Para tal, exemplico com a seguinte regra:

RewriteRule ^/agenda/(([0-3]+[0-9])(.*)).html$ /agenda.php?data=$1

Repare que agora a expressão para um mês de 39 dias segue com “.*”. Isso significa que qualquer coisa a partir daquele ponto é permitida, desde que seguida pela extensão html. A barra invertida foi utilizada para quebrar o significado da expressão de “.”, que indica a posição atual. Com a barra invertida, ponto significa ponto mesmo. E ponto.
Para finalizar, o redirecionamento aponta para a variável data, que recebe o que casar com a expressão como parâmetro.
Também deve-se notar que toda a expressão foi fechada em parênteses, senão teriamos 2 parâmetros em data; $1 com o dia e $2 com o restante da string; agora o parâmetro $2 seria apenas a extensão (‘.html’).

Uma url assim seria válida para esse caso:

http://algumacoisa.com/agenda/09-02-12.html
#resultaria em:
http://algumacoisa.com/agenda.php?data=09-02-12

Obviamente estes exemplos também casariam:

http://algumacoisa.com/agenda/09-Fevereiro-2012.html
http://algumacoisa.com/agenda/09-Banana-2012.html

Ainda está simples, mas as vezes as expressões regulares podem nos pregar peças e alguns resultados podem ser imprevisiveis. Então, a regra anterior necessita de mais requinte para tratar somente dados numéricos representando data, com até 2 bytes de comprimento e utilizando o separador “-“, porque no formato que está é uma regra falha.

Flags

Estou seguindo os mesmos passos do primeiro tutorial, inserindo mais ou menos informações conforme a ocasião.
Flags são utilizadas para alterar o comportamento da reescrita. As mais comuns são:
[L] – Last. Quando contida ao final de uma linha, o rewrite para o processamento nessa regra.
[R] – Redirect. Utilizada para enviar um código de redirect para o usuário.
[QSA] – Query String Append. Adiciona sua query string ao dado que já veio no get.
[NC] – No Case. Torna a expressão insensitiva.

Existem muitas outras flags para coisas mais específicas, mas vou seguir a âncora da referência; não quero lhe aborrecer com detalhes!

Enfim, aplicando algumas flags como exemplo (ainda está falha a regra):

RewriteRule ^/agenda/(([0-3]+[0-9])(.*)).html$ /agenda.php?data=$1 [NC,L]

Nesse caso, o que casar com a expressão recebe o redirecionamento e o trabalho do rewrite para essa requisição termina aqui. Alguns exemplos que casariam:

http://algumacoisa.com/agenda/09-FeveReiRo-12.html
http://algumacoisa.com/agenda/09-fevereiro-12.html
http://algumacoisa.com/agenda/09-02-12.html

Reforço que no exemplo estou citando apenas os resultados esperados; diversas coisas inválidas casariam com essa regra.

RewriteCond
RewriteCond permite escrever condições que devem casar para que uma decisão seja tomada. Seu formato é:

RewriteCond STRING CONDITION

A STRING pode ser uma variável do servidor (vide lista de variáveis) ou uma backreference de outra RewriteCond.
A CONDITION pode ser outra expressão regular similar ao RewriteRule.

Então, uma RewriteRule só será executada se uma RewriteCond for atendida, se composta a regra dessa maneira:

RewriteCond %{REMOTE_ADDR} 123.45.67.89
RewriteRule .* limbo.html [R]

Nesse exemplo, a condição é que o host remoto tenha o IP 123.45.67.89. A regra que segue é “qualquer coisa” (.*), não importa o que seja, envie-o para o limbo(.html). Essa página pode conter qualquer mensagem como “você não pode ver nada aqui, mané”.

Algumas variáveis do servidor

HTTP_USER_AGENT
HTTP_REFERER
HTTP_HOST
SERVER_PORT
REMOTE_ADDR
SCRIPT_FILENAME
QUERY_STRING
REQUEST_URI

Esse material estava em meu rascunho já há algum tempo e não tive tempo ainda de elaborá-lo melhor, mas prometo que em algum tempo eu escrevo a parte 2 do mod_rewrite. Com certeza essa introdução lhe dará estrutura para avançar em diferentes exemplos. Bons testes!

Comments

comments

Djames Suhanko

Djames Suhanko é Perito Forense Digital. Já atuou com deployer em sistemas de missão critica em diversos países pelo mundão. Programador Shell, Python, C, C++ e Qt, tendo contato com embarcados ( ora profissionalmente, ora por lazer ) desde 2009.

Deixe uma resposta