Referência: mod_rewrite, reescrita de URL e "links bonitos" explicados

142

"Links bonitos" é um tópico frequentemente solicitado, mas raramente é totalmente explicado. O mod_rewrite é uma maneira de criar "links bonitos", mas é complexo e sua sintaxe é muito concisa, difícil de entender, e a documentação pressupõe um certo nível de proficiência em HTTP. Alguém pode explicar em termos simples como "links bonitos" funcionam e como mod_rewrite pode ser usado para criá-los?

Outros nomes comuns, aliases, termos para URLs limpos: URLs RESTful , URLs amigáveis ​​ao usuário, URLs amigáveis ​​ao SEO , slugging e URLs MVC (provavelmente um nome impróprio)

deceze
fonte
2
Slug ou Slugging é outro alias / termo comum para URLs bonitas.
Mike B
2
@ Mike Mais ou menos, mas lesmas geralmente fazem parte de URLs bonitas. Uma lesma é bastante específica quando, por exemplo, o título de um artigo é transformado em um formato compatível com URL, que atua como o identificador desse artigo. Assim reference-mod-rewrite-url-rewriting-explainedé a lesma, /questions/20563772/reference-mod-rewrite-url-rewriting-explainedé a URL bonita.
Deceze
2
Eu acho que as tags .htaccesse mod-rewritedevem ser atualizadas para incluir um link para essa pergunta, pois abrange muito do que é solicitado regularmente. Pensamentos?
Mike Rockétt

Respostas:

110

Para entender qual mod_rewrite você precisa primeiro entender como um servidor da web funciona. Um servidor da web responde às solicitações HTTP . Uma solicitação HTTP em seu nível mais básico se parece com isso:

GET /foo/bar.html HTTP/1.1

Essa é a simples solicitação de um navegador para um servidor da Web que solicita a URL /foo/bar.html dele. É importante ressaltar que ele não solicita um arquivo , solicita apenas uma URL arbitrária. A solicitação também pode ser assim:

GET /foo/bar?baz=42 HTTP/1.1

Essa solicitação é igualmente válida para um URL e obviamente não tem nada a ver com arquivos.

O servidor da Web é um aplicativo que atende a uma porta, aceitando solicitações HTTP que entram nessa porta e retornando uma resposta. Um servidor da Web é totalmente livre para responder a qualquer solicitação da maneira que achar melhor / da maneira que você configurou para responder. Esta resposta não é um arquivo, é uma resposta HTTP que pode ou não ter nada a ver com arquivos físicos em qualquer disco. Um servidor web não precisa ser o Apache, existem muitos outros servidores web, todos programas que são executados persistentemente e estão conectados a uma porta que responde a solicitações HTTP. Você pode escrever um você mesmo. Este parágrafo tinha como objetivo separar você de qualquer noção de que os URLs sejam iguais a arquivos diretamente, o que é realmente importante de entender. :)

A configuração padrão da maioria dos servidores da Web é procurar um arquivo que corresponda à URL no disco rígido. Se a raiz do documento do servidor estiver configurada para, por exemplo /var/www, ele poderá verificar se o arquivo /var/www/foo/bar.htmlexiste e servi-lo, se houver. Se as extremidades de arquivo em ".php" que irá chamar o intérprete PHP e , em seguida, retornar o resultado. Toda essa associação é completamente configurável; um arquivo não precisa terminar em ".php" para o servidor Web executá-lo através do interpretador PHP, e a URL não precisa corresponder a nenhum arquivo específico no disco para que algo aconteça.

mod_rewrite é uma maneira de reescrever o tratamento de solicitações internas. Quando o servidor da web recebe uma solicitação para a URL /foo/bar, você pode reescrevê-la em outra coisa antes que o servidor procure um arquivo no disco para corresponder a ele. Exemplo simples:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

Esta regra diz que sempre que uma solicitação corresponder a "/ foo / bar", reescreva-a em "/ foo / baz". A solicitação será tratada como se /foo/baztivesse sido solicitada. Isso pode ser usado para vários efeitos, por exemplo:

RewriteRule (.*) $1.html

Essa regra corresponde a qualquer coisa ( .*) e a captura ( (..)), depois a reescreve para acrescentar ".html". Em outras palavras, se /foo/barfoi o URL solicitado, ele será tratado como se /foo/bar.htmltivesse sido solicitado. Consulte http://regular-expressions.info para obter mais informações sobre correspondência, captura e substituição de expressões regulares.

Outra regra frequentemente encontrada é esta:

RewriteRule (.*) index.php?url=$1

Novamente, isso corresponde a qualquer coisa e a reescreve no arquivo index.php com a URL solicitada originalmente anexada no urlparâmetro de consulta. Ou seja, para todos e quaisquer pedidos que chegam, o arquivo index.php é executado e esse arquivo terá acesso ao pedido original $_GET['url'], para que ele possa fazer o que quiser com ele.

Primeiramente, você coloca essas regras de reescrita no arquivo de configuração do servidor da web . O Apache também permite * que você os coloque em um arquivo chamado .htaccessdentro da raiz do documento (ou seja, próximo aos arquivos .php).

* Se permitido pelo arquivo de configuração principal do Apache; é opcional, mas geralmente ativado.

O que mod_rewrite não faz

mod_rewrite não torna magicamente todos os seus URLs "bonitos". Este é um mal-entendido comum. Se você possui este link no seu site:

<a href="https://stackoverflow.com/my/ugly/link.php?is=not&amp;very=pretty">

não há nada que o mod_rewrite possa fazer para tornar isso bonito. Para fazer deste um link bonito, você deve:

  1. Mude o link para um link bonito:

    <a href="https://stackoverflow.com/my/pretty/link">
    
  2. Use mod_rewrite no servidor para manipular a solicitação para a URL /my/pretty/linkusando qualquer um dos métodos descritos acima.

(Pode-se usar mod_substituteem conjunto para transformar páginas HTML de saída e seus links contidos. Embora isso seja geralmente mais esforço do que apenas atualizar seus recursos HTML.)

Há muito o que mod_rewrite pode fazer e regras de correspondência muito complexas que você pode criar, incluindo encadear várias reescritas, solicitações de proxy para um serviço ou máquina completamente diferente, retornar códigos de status HTTP específicos como respostas, redirecionar solicitações etc. É muito poderoso e pode ser usado para ótimo se você entender o mecanismo fundamental de solicitação e resposta HTTP. Ele não torna seus links automaticamente bonitos.

Consulte a documentação oficial para todos os sinalizadores e opções possíveis.

deceze
fonte
6
Talvez mencione a diretiva FallbackResource introduzida na versão 2.2.16 como a maneira preferida de reescrever para um distribuidor.
Darsstar
78

Para expandir a resposta de deceze , eu queria fornecer alguns exemplos e explicações sobre outras funcionalidades do mod_rewrite.

Todos os exemplos abaixo assumem que você já incluiu RewriteEngine Onno seu .htaccessarquivo.

Reescrever Exemplo

Vamos pegar este exemplo:

RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]

A regra é dividida em 4 seções:

  1. RewriteRule - inicia a regra de reescrita
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - Isso é chamado de padrão, no entanto, vou me referir a ele como o lado esquerdo da regra - do que você deseja reescrever
  3. blog/index.php?id=$1&title=$2 - chamado de substituição, ou lado direito de uma regra de reescrita - no que você deseja reescrever
  4. [NC,L,QSA] são sinalizadores para a regra de reescrita, separados por vírgula, que explicarei mais adiante

A reescrita acima permitiria que você vinculasse a algo assim /blog/1/foo/e, na verdade, seria carregado /blog/index.php?id=1&title=foo.

Lado esquerdo da regra

  • ^indica o início do nome da página - para reescrever, example.com/blog/...mas não paraexample.com/foo/blog/...
  • Cada conjunto de (…)parênteses representa uma expressão regular que podemos capturar como uma variável no lado direito da regra. Neste exemplo:
    • O primeiro conjunto de colchetes - ([0-9]+)- corresponde a uma sequência com no mínimo 1 caractere de comprimento e apenas com valores numéricos (ou seja, de 0 a 9). Isso pode ser referenciado $1no lado direito da regra
    • O segundo conjunto de parênteses corresponde a uma cadeia de caracteres com um comprimento mínimo de 1 caractere, contendo apenas caracteres alfanuméricos (AZ, az ou 0-9) ou -ou +(a nota +é escapada com uma barra invertida, pois, sem escape, isso será executado como uma expressão regular caráter de repetição ). Isso pode ser referenciado $2no lado direito da regra
  • ?significa que o caractere anterior é opcional, portanto, neste caso, ambos /blog/1/foo/e /blog/1/fooreescreveriam no mesmo local
  • $ indica que este é o fim da string que queremos corresponder

Bandeiras

Essas são as opções adicionadas entre colchetes no final de sua regra de reescrita para especificar determinadas condições. Novamente, existem muitos sinalizadores diferentes que você pode ler na documentação , mas analisarei alguns dos sinalizadores mais comuns:

NC

O sinalizador no case significa que a regra de reescrita não faz distinção entre maiúsculas e minúsculas; portanto, para a regra de exemplo acima, isso significaria que ambos /blog/1/foo/e /BLOG/1/foo/(ou qualquer variação disso) seriam correspondidos.

L

O último sinalizador indica que esta é a última regra que deve ser processada. Isso significa que, se e somente se essa regra corresponder, nenhuma regra adicional será avaliada na execução do processamento de reescrita atual. Se a regra não corresponder, todas as outras regras serão tentadas em ordem, como de costume. Se você não definir o Lsinalizador, todas as regras a seguir serão aplicadas ao URL reescrito posteriormente.

END

Desde o Apache 2.4, você também pode usar a [END]bandeira. Uma regra correspondente terminará completamente o processamento de alias / reescrita. (Enquanto o [L]sinalizador muitas vezes pode acionar uma segunda rodada, por exemplo, quando reescrever dentro ou fora de subdiretórios.)

QSA

O sinalizador de acréscimo da cadeia de consulta nos permite passar variáveis ​​extras para o URL especificado, que será adicionado aos parâmetros de obtenção originais. Para o nosso exemplo, isso significa que algo como /blog/1/foo/?comments=15carregaria/blog/index.php?id=1&title=foo&comments=15

R

Essa bandeira não é a que usei no exemplo acima, mas achei que vale a pena mencionar. Isso permite que você especifique um redirecionamento http, com a opção de incluir um código de status (por exemplo R=301). Por exemplo, se você quisesse fazer um redirecionamento 301 em / myblog / to / blog /, você simplesmente escreveria uma regra mais ou menos assim:

RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]

Condições de reescrita

As condições de reescrita tornam as reescritas ainda mais poderosas, permitindo especificar reescritas para situações mais específicas. Você pode ler sobre muitas condições na documentação , mas abordarei alguns exemplos comuns e os explicarei:

# if the host doesn't start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Essa é uma prática muito comum, que antecederá seu domínio www.(se já não estiver lá) e executará um redirecionamento 301. Por exemplo, carregá- http://example.com/blog/lo o redirecionaria parahttp://www.example.com/blog/

# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]

Isso é um pouco menos comum, mas é um bom exemplo de regra que não é executada se o nome do arquivo for um diretório ou arquivo que existe no servidor.

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] executará apenas a reescrita de arquivos com uma extensão de arquivo jpg, jpeg, gif ou png (sem distinção entre maiúsculas e minúsculas).
  • %{REQUEST_FILENAME} !-f verificará se o arquivo existe no servidor atual e só executará a reescrita se não existir
  • %{REQUEST_FILENAME} !-d verificará se o arquivo existe no servidor atual e só executará a reescrita se não existir
  • A reescrita tentará carregar o mesmo arquivo em outro domínio
usuario
fonte
39

Referências

O Stack Overflow possui muitos outros ótimos recursos para começar:

E visões gerais sobre regex para iniciantes, mesmo:

Espaços reservados frequentemente usados

  • .*corresponde a qualquer coisa, até mesmo uma string vazia. Você não deseja usar esse padrão em qualquer lugar, mas frequentemente na última regra de fallback.
  • [^/]+é mais frequentemente usado para segmentos de caminho. Corresponde a qualquer coisa, menos à barra.
  • \d+ corresponde apenas a cadeias numéricas.
  • \w+corresponde a caracteres alfanuméricos. É basicamente uma abreviação de [A-Za-z0-9_].
  • [\w\-]+para segmentos de caminho no estilo "lesma", usando letras, números, traço - e _
  • [\w\-.,]+adiciona pontos e vírgulas. Prefira um \-traço escapado nas […]charclasses.
  • \.denota um período literal. Caso contrário, .fora de […]é um espaço reservado para qualquer símbolo.

Cada um desses espaços reservados geralmente é colocado entre (…)parênteses como grupo de captura. E todo o padrão geralmente nos ^………$marcadores de início e fim. Citar "padrões" é opcional.

RewriteRules

Os exemplos a seguir são centrados no PHP e um pouco mais incrementais, mais fáceis de adaptar para casos semelhantes. São apenas resumos, geralmente vinculados a mais variações ou perguntas e respostas detalhadas.

  • Mapeamento estático
    /contact,/about

    Reduzir alguns nomes de página para esquemas de arquivos internos é mais simples:

     RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    
  • Identificadores numéricos
    /object/123

    A introdução de atalhos como http://example.com/article/531os scripts PHP existentes também é fácil. O espaço reservado numérico pode ser remapeado apenas para um $_GETparâmetro:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    
  • Espaços reservados no estilo lesma
    /article/with-some-title-slug

    Você pode estender facilmente essa regra para permitir /article/title-stringespaços reservados:

     RewriteRule ^article/([\w-]+)$    article-show.php?title=$1
     #                       └────────────────────────────────┘
    

    Observe que seu script deve poder (ou ser adaptado) mapear esses títulos de volta para os IDs do banco de dados. Somente o RewriteRules não pode criar ou adivinhar informações do nada.

  • Lesmas com prefixos numéricos
    /readable/123-plus-title

    Portanto, você verá frequentemente /article/529-title-slugcaminhos mistos usados ​​na prática:

     RewriteRule ^article/(\d+)-([\w-]+)$    article.php?id=$1&title=$2
     #                      └───────────────────────────────┘
    

    Agora você pode simplesmente ignorar a passagem de title=$2qualquer maneira, porque seu script normalmente depende do ID do banco de dados de qualquer maneira. A -title-slugdecoração de URL se tornou arbitrária.

  • Uniformidade com listas alternativas
    /foo/… /bar/… /baz/…

    Se você tiver regras semelhantes para vários caminhos de página virtual, poderá combiná-las e compactá-las com |listas alternativas. E, novamente, apenas reatribua-os aos parâmetros GET internos:

     #                               ┌─────────────────────────┐
     RewriteRule ^(blog|post|user)/(\w+)$  disp.php?type=$1&id=$2
     #               └───────────────────────────────────┘
    

    Você pode dividi-los em RewriteRules individuais, se isso for muito complexo.

  • Envio de URLs relacionados para diferentes back-end
    /date/SWITCH/backend

    Um uso mais prático de listas alternativas é o mapeamento de caminhos de solicitação para scripts distintos. Por exemplo, para fornecer URLs uniformes para um aplicativo Web mais antigo e mais novo, com base nas datas:

     #                   ┌─────────────────────────────┐
     #                   │                 ┌───────────┼───────────────┐
     RewriteRule ^blog/(2009|2010|2011)/([\d-]+)/?$ old/blog.php?date=$2
     RewriteRule ^blog/(\d+)/([\d-]+)/?$  modern/blog/index.php?start=$2
     #                          └──────────────────────────────────────┘
    

    Isso simplesmente remapeia as postagens de 2009-2011 em um script e todos os outros anos implicitamente em outro manipulador. Observe a regra mais específica que vem primeiro . Cada script pode usar diferentes parâmetros GET.

  • Outros delimitadores que não sejam apenas /barras
    /user-123-name

    Você costuma ver RewriteRules para simular uma estrutura de diretório virtual. Mas você não é forçado a não ser criativo. Você também pode usar -hífens para segmentação ou estrutura.

     RewriteRule ^user-(\d+)$    show.php?what=user&id=$1
     #                   └──────────────────────────────┘
     # This could use `(\w+)` alternatively for user names instead of ids.
    

    Para o /wiki:section:Page_Nameesquema também comum :

     RewriteRule ^wiki:(\w+):(\w+)$  wiki.php?sect=$1&page=$2 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    Ocasionalmente, é adequado alternar entre /-delimitadores e :ou .na mesma regra mesmo. Ou tenha duas RewriteRules novamente para mapear variantes em scripts diferentes.

  • À direita opcional /barra
    /dir=/dir/

    Ao optar por caminhos no estilo de diretório, você pode torná-lo acessível com e sem um final /

     RewriteRule ^blog/([\w-]+)/?$  blog/show.php?id=$1
     #                         ┗┛
    

    Agora isso lida com ambos http://example.com/blog/123e /blog/123/. E /?$é fácil anexar a abordagem a qualquer outra RewriteRule.

  • Segmentos flexíveis para caminhos virtuais
    .*/.*/.*/.*

    A maioria das regras encontradas mapeará um conjunto restrito de /…/segmentos do caminho do recurso para parâmetros GET individuais. Alguns scripts lidam com um número variável de opções, no entanto. O mecanismo regexp do Apache não permite opcionalizar um número arbitrário deles. Mas você pode expandi-lo facilmente para uma regra:

     Rewriterule ^(\w+)/?$                in.php?a=$1
     Rewriterule ^(\w+)/(\w+)/?$          in.php?a=$1&b=$2
     Rewriterule ^(\w+)/(\w+)/(\w+)/?$    in.php?a=$1&b=$2&c=$3
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    Se você precisar de até cinco segmentos de caminho, copie esse esquema em cinco regras. Obviamente, você pode usar um [^/]+espaço reservado mais específico para cada um. Aqui, a encomenda não é tão importante quanto nenhuma das duas se sobrepõe. Portanto, ter os caminhos mais usados ​​primeiro é bom.

    Como alternativa, você pode utilizar os parâmetros da matriz do PHPs via ?p[]=$1&p[]=$2&p[]=3string de consulta aqui - se o seu script simplesmente os preferir antes da divisão. (Embora seja mais comum usar apenas uma regra geral, e deixar o próprio script expandir os segmentos fora do REQUEST_URI.)

    Consulte também: Como transformar meus segmentos de caminho da URL em pares de valores-chave da cadeia de consulta?

  • Segmentos opcionais
    prefix/opt?/.*

    Uma variação comum é ter prefixos opcionais dentro de uma regra. Isso geralmente faz sentido se você tiver cadeias estáticas ou espaços reservados mais restritos ao redor:

      RewriteRule ^(\w+)(?:/([^/]+))?/(\w+)$  ?main=$1&opt=$2&suffix=$3
    

    Agora, o padrão mais complexo (?:/([^/])+)?simplesmente envolve um grupo que não captura (?:…) e o torna opcional )?. O espaço reservado contido ([^/]+)seria o padrão de substituição $2, mas ficará vazio se não houver /…/caminho do meio .

  • Capturar o restante
    /prefix/123-capture/…/*/…whatever…

    Como dito anteriormente, muitas vezes você não deseja padrões de reescrita genéricos demais. No entanto, faz sentido combinar comparações estáticas e específicas com .*algumas vezes.

     RewriteRule ^(specific)/prefix/(\d+)(/.*)?$  speci.php?id=$2&otherparams=$2
    

    Isso opcionalizou todos /…/…/…os segmentos de caminho à direita. O que, é claro, exige que o script de manipulação os divida, e a variável extraiu os próprios parâmetros (que é o que as estruturas Web-"MVC" fazem).

  • Trailing file "extensions"
    /old/path.HTML

    Os URLs realmente não têm extensões de arquivo. É disso que trata toda essa referência (= URLs são localizadores virtuais, não necessariamente uma imagem direta do sistema de arquivos). No entanto, se você já tinha um mapeamento de arquivo 1: 1, pode criar regras mais simples:

     RewriteRule  ^styles/([\w\.\-]+)\.css$  sass-cache.php?old_fn_base=$1
     RewriteRule  ^images/([\w\.\-]+)\.gif$  png-converter.php?load_from=$2
    

    Outros usos comuns são remapear .htmlcaminhos obsoletos para .phpmanipuladores mais novos ou apenas aliasizar nomes de diretório apenas para arquivos individuais (reais / reais).

  • Ping-Pong (redireciona e reescreve em uníssono)
    /ugly.html← →/pretty

    Então, em algum momento, você está reescrevendo suas páginas HTML para conter apenas links bonitos, conforme descrito por deceze . Enquanto isso, você ainda receberá solicitações dos caminhos antigos , às vezes até de favoritos. Como solução alternativa , você pode usar os navegadores de pingue-pongue para exibir / estabelecer os novos URLs.

    Esse truque comum envolve o envio de um redirecionamento 30x / Location sempre que um URL de entrada segue o esquema de nomeação obsoleto / feio. Os navegadores solicitarão novamente a URL nova / bonita, que depois será reescrita (apenas internamente) no local original ou novo.

     # redirect browser for old/ugly incoming paths
     RewriteRule ^old/teams\.html$ /teams [R=301,QSA,END]
    
     # internally remap already-pretty incoming request
     RewriteRule ^teams$ teams.php        [QSA,END]
    

    Observe como este exemplo usa apenas em [END]vez de [L]alternar com segurança. Para versões mais antigas do Apache 2.2, você pode usar outras soluções alternativas, além de remapear os parâmetros da string de consulta, por exemplo: Redirecionar URL feio para bonito, remapear de volta para o caminho feio, sem loops infinitos

  • Espaços em padrões
    /this+that+

    Não é tão bonito nas barras de endereço do navegador, mas você pode usar espaços nos URLs. Para reescrever padrões, use \␣espaços com barras invertidas . "Caso contrário, apenas cite todo o padrão ou substituição:

     RewriteRule  "^this [\w ]+/(.*)$"  "index.php?id=$1"  [L]
    

    Os clientes serializam URLs com +ou %20para espaços. No entanto, em RewriteRules, eles são interpretados com caracteres literais para todos os segmentos de caminho relativos.

Duplicatas frequentes:

prevalentes .htaccessarmadilhas

Agora leve isso com um grão de sal. Nem todo conselho pode ser generalizado para todos os contextos. Este é apenas um resumo simples de alguns obstáculos conhecidos e pouco óbvios:

  • Ativar mod_rewritee.htaccess

    Para realmente usar RewriteRules nos arquivos de configuração por diretório, você deve:

    • Verifique se o seu servidor está AllowOverride Allativado . Caso contrário, suas .htaccessdiretivas por diretório serão ignoradas e RewriteRules não funcionará.

    • Obviamente, ter mod_rewritehabilitado em sua httpd.confseção de módulos.

    • Anexe cada lista de regras com RewriteEngine Onainda. Embora o mod_rewrite esteja implicitamente ativo nas seções <VirtualHost>e <Directory>, os .htaccessarquivos por diretório precisam ser convocados individualmente.

  • A barra principal ^/não corresponde

    Você não deve iniciar seus .htaccesspadrões RewriteRule ^/normalmente:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    Isso geralmente é visto em tutoriais antigos. E costumava estar correto para as versões antigas do Apache 1.x. Atualmente, os caminhos de solicitação são convenientemente totalmente relativos ao diretório em .htaccessRewriteRules. Apenas deixe o líder de /fora.

    · Observe que a barra principal ainda está correta nas <VirtualHost>seções. É por isso que você costuma vê-lo ^/?opcionalizado por paridade de regras.
    · Ou ao usar um, RewriteCond %{REQUEST_URI}você ainda corresponderia a um líder /.
    · Consulte também Webmaster.SE: quando é a barra inicial (/) necessária nos padrões mod_rewrite?

  • <IfModule *> invólucros começar!

    Você provavelmente já viu isso em muitos exemplos:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • Ele faz fazem sentido em <VirtualHost>seções - se é que foi combinado com outra opção de recurso, como ScriptAliasMatch. (Mas ninguém nunca faz isso).
    • E é geralmente distribuído para .htaccessconjuntos de regras padrão com muitos projetos de código aberto. Lá, ele é apenas um substituto e mantém os URLs "feios" como padrão.

    No entanto, você não quer isso normalmente em seus próprios .htaccessarquivos.

    • Em primeiro lugar, o mod_rewrite não é desativado aleatoriamente. (Se o fizesse, você teria problemas maiores).
    • Se realmente fosse desativado, suas RewriteRules ainda não funcionariam.
    • Destina-se a evitar 500erros de HTTP . Em geral, o que ele realiza é agregar seus usuários com 404erros de HTTP . (Não é muito mais fácil de usar se você pensar a respeito.)
    • Na prática, apenas suprime as entradas de log mais úteis ou os emails de notificação do servidor. Você seria nenhum o mais sábio a respeito de porque seus RewriteRules nunca funcionam.

    O que parece atraente como salvaguarda generalizada, muitas vezes acaba sendo um obstáculo na prática.

  • Não use a RewriteBasemenos que necessário

    Muitos exemplos de copiar e colar contêm uma RewriteBase /diretiva. O que passa a ser o padrão implícito de qualquer maneira. Então você realmente não precisa disso. É uma solução alternativa para esquemas sofisticados de reescrita do VirtualHost e caminhos DOCUMENT_ROOT equivocados para alguns hosts compartilhados.

    Faz sentido usar com aplicativos da web individuais em subdiretórios mais profundos. Pode reduzir os padrões RewriteRule nesses casos. Geralmente, é melhor preferir especificadores de caminho relativo em conjuntos de regras por diretório.

    Consulte também Como o RewriteBase funciona em .htaccess

  • Desativar MultiViewsquando caminhos virtuais se sobrepõem

    A reescrita de URL é usada principalmente para oferecer suporte a caminhos de entrada virtuais . Comumente você só tem um roteiro despachante ( index.php) ou alguns manipuladores individuais ( articles.php, blog.php, wiki.php, ...). O último pode colidir com caminhos RewriteRule virtuais semelhantes.

    Uma solicitação, por /article/123exemplo, poderia mapear para article.phpcom um /123PATH_INFO implicitamente. Você teria que guardar suas regras com o lugar-comum RewriteCond !-f+ !-de / ou desativar o suporte PATH_INFO, ou talvez apenas desativar Options -MultiViews.

    O que não quer dizer que você sempre precise . Negociação de conteúdo é apenas um automatismo para recursos virtuais.

  • Encomendar é importante

    Veja Tudo o que você sempre quis saber sobre mod_rewrite, se ainda não o fez. A combinação de várias RewriteRules geralmente leva à interação. Isso não é algo a ser evitado habitualmente por [L]bandeira, mas um esquema que você adotará uma vez versado. Você pode re-re-re write caminhos virtuais a partir de uma regra para outro, até que ele atinja um manipulador alvo real.

    Ainda que você muitas vezes querem ter o maior número de regras específicas (string fixa /forum/…padrões, ou espaços reservados mais restritivas [^/.]+) nas primeiras regras. Regras slurp-all genéricas ( .*) são melhor deixadas para as posteriores . (Uma exceção é uma RewriteCond -f/-dproteção como bloco principal.)

  • Folhas de estilo e imagens param de funcionar

    Quando você introduz estruturas de diretório virtual, /blog/article/123isso afeta as referências de recursos relativos em HTML (como <img src=mouse.png>). Que pode ser resolvido por:

    • Apenas usando referências absolutas do servidor href="https://stackoverflow.com/old.html"ousrc="/logo.png"
    • Geralmente, basta adicionar <base href="https://stackoverflow.com/index">à sua <head>seção HTML . Isso implicitamente religa referências relativas ao que eram antes.

    Como alternativa, você pode criar RewriteRules adicionais para religar .cssou .pngcaminhos para seus locais originais. Mas isso é desnecessário ou gera redirecionamentos extras e dificulta o armazenamento em cache.

    Veja também: CSS, JS e imagens não são exibidas com URL bonito

  • RewriteConds apenas mascara uma RewriteRule

    Um erro de interpretação comum é que um RewriteCond bloqueia várias RewriteRules (porque elas são visualmente organizadas):

     RewriteCond %{SERVER_NAME} localhost
     RewriteRule ^secret  admin/tools.php
     RewriteRule ^hidden  sqladmin.cgi
    

    O que não ocorre por padrão. Você pode encadeá-los usando a [S=2]bandeira. Senão você terá que repeti-los. Embora às vezes você possa criar uma regra primária "invertida" para [END] o processo de reescrita mais cedo.

  • QUERY_STRING isento de RewriteRules

    Você não pode corresponder RewriteRule index.php\?x=y, porque mod_rewrite se compara apenas aos caminhos relativos por padrão. No entanto, você pode combiná-los separadamente via:

     RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$)
     RewriteRule ^add/(.+)$  add/%1/$1  # ←──﹪₁──┘
    

    Consulte também Como posso combinar variáveis ​​de string de consulta com mod_rewrite?

  • .htaccess vs. <VirtualHost>

    Se você estiver usando RewriteRules em um arquivo de configuração por diretório, não será necessário se preocupar com o desempenho do regex. O Apache mantém padrões PCRE compilados por mais tempo que um processo PHP com uma estrutura de roteamento comum. Para sites de alto tráfego, você deve considerar a mudança de conjuntos de regras para a configuração do servidor vhost, depois de testados em batalha.

    Nesse caso, prefira o ^/?prefixo do separador de diretório opcionalizado . Isso permite mover RewriteRules livremente entre o PerDir e os arquivos de configuração do servidor.

  • Sempre que algo não funciona

    Não se preocupe.

    • Compare access.logeerror.log

      Muitas vezes, você pode descobrir como um RewriteRule se comporta mal apenas olhando para você error.loge access.log. Correlacione os tempos de acesso para ver qual caminho da solicitação veio originalmente e qual caminho / arquivo o Apache não conseguiu resolver (erro 404/500).

      Isso não informa qual RewriteRule é o culpado. Mas caminhos finais inacessíveis como esses /docroot/21-.itle?index.phppodem indicar onde inspecionar mais. Caso contrário, desative as regras até obter alguns caminhos previsíveis.

    • Habilitar o RewriteLog

      Consulte os documentos do Apache RewriteLog . Para depuração, você pode habilitá-lo nas seções vhost:

      # Apache 2.2
      RewriteLogLevel 5
      RewriteLog /tmp/rewrite.log
      
      # Apache 2.4
      LogLevel alert rewrite:trace5
      #ErrorLog /tmp/rewrite.log
      

      Isso produz um resumo detalhado de como os caminhos de solicitação recebidos são modificados por cada regra:

      [..] applying pattern '^test_.*$' to uri 'index.php'
      [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
      [..] applying pattern '^index\.php$' to uri 'index.php'
      

      O que ajuda a restringir regras excessivamente genéricas e contratempos de regex.

      Consulte também:
      · .htaccess não está funcionando (mod_rewrite)
      · Dicas para depuração de regras de reescrita .htaccess

    • Antes de fazer sua própria pergunta

      Como você deve saber, o Stack Overflow é muito adequado para fazer perguntas no mod_rewrite. Torná-los no tópico , incluindo pesquisas e tentativas anteriores (evitar respostas redundantes), demonstrar compreensão e:

      • Inclua exemplos completos de URLs de entrada, caminhos de destino reescritos falsamente, sua estrutura de diretório real.
      • O conjunto completo de RewriteRule, mas também destaca o suposto defeito.
      • Versões Apache e PHP, tipo de SO, sistema de arquivos, DOCUMENT_ROOT e $_SERVERambiente de PHPs , se houver uma incompatibilidade de parâmetro.
      • Um trecho seu access.loge error.logpara verificar o que as regras existentes resolveram. Melhor ainda, um rewrite.logresumo.

      Isso gera respostas mais rápidas e exatas e as torna mais úteis para os outros.

  • Comente seu .htaccess

    Se você copiar exemplos de algum lugar, lembre-se de incluir a # comment and origin link. Embora seja apenas falta de educação omitir a atribuição, muitas vezes prejudica a manutenção mais tarde. Documente qualquer código ou fonte de tutorial. Em particular, enquanto não é monitorado, você deve estar ainda mais interessado em não tratá-los como caixas-pretas mágicas.

  • Não são URLs de "SEO"

    Disclaimer: Apenas uma irritação. Você costuma ouvir esquemas de reescrita de URL conhecidos como links "SEO" ou algo assim. Embora isso seja útil para exemplos no Google, é um nome impróprio datado.

    Nenhum dos motores de busca modernos são realmente perturbado por .htmle .phpem segmentos de caminho, ou ?id=123seqüências de consulta para esse assunto. Os motores de busca de idade, tais como AltaVista, fez evitar o rastreamento de sites com caminhos de acesso potencialmente ambígua. Os rastreadores modernos muitas vezes desejam até recursos profundos da Web.

    Como URLs "bonitas" devem ser usadas conceitualmente para tornar os sites fáceis de usar .

    1. Ter esquemas de recursos legíveis e óbvios.
    2. Garantir que os URLs tenham vida longa (AKA permalinks ).
    3. Fornecendo descoberta através /common/tree/nesting.

    No entanto, não sacrifique requisitos exclusivos de conformidade.

Ferramentas

Existem várias ferramentas online para gerar RewriteRules para a maioria dos URLs com parâmetros GET:

Principalmente, apenas [^/]+gera espaços reservados genéricos, mas provavelmente é suficiente para sites triviais.

mario
fonte
Ainda precisa de um pouco de reescrita, mais links, e os muitos subtítulos são um tanto desagradáveis. Há alguma sobreposição com as outras respostas aqui, então talvez isso possa ser diminuído. É principalmente sobre os exemplos visuais e essa lista de truques comuns.
Mario
3
Não via a beleza de uma resposta por um longo tempo! Meus olhos estão brilhando enquanto estou lendo. Por favor, não parar de postar tais respostas :)
Rizier123
1
Excelente post. Me fez entender os conceitos básicos de mod_rewrite muito rapidamente!
breez
6

Alternativas ao mod_rewrite

Muitos esquemas básicos de URL virtual podem ser alcançados sem o uso de RewriteRules. O Apache permite que scripts PHP sejam chamados sem .phpextensão e com um PATH_INFOargumento virtual .

  1. Use o PATH_INFO , Luke

    Atualmente, AcceptPathInfo Onmuitas vezes é ativado por padrão. O que basicamente permite que .phpoutros URLs de recursos carreguem um argumento virtual:

    http://example.com/script.php/virtual/path
    

    Agora isso /virtual/pathaparece no PHP como$_SERVER["PATH_INFO"] onde você pode lidar com qualquer argumento extra da maneira que desejar.

    Isto não é tão conveniente como tendo Apache segmentos de caminho de entrada separada para $1, $2, $3e passá-los como distintos $_GETvariáveis para PHP. É apenas emular "URLs bonitas" com menos esforço de configuração.

  2. Ative o MultiViews para ocultar o.php extensão

    A opção mais simples de evitar também .php"extensões de arquivo" em URLs é habilitar:

    Options +MultiViews
    

    Isso faz com que o Apache selecione article.phpsolicitações HTTP /articledevido ao nome de base correspondente. E isso funciona bem em conjunto com o recurso PATH_INFO acima mencionado. Então você pode simplesmente usar URLs comohttp://example.com/article/virtual/title . O que faz sentido se você tiver um aplicativo Web tradicional com vários pontos / scripts de chamada PHP.

    Observe que o MultiViews tem um propósito diferente / mais amplo. Ele incorre em uma penalidade de desempenho muito menor , porque o Apache sempre procura outros arquivos com nomes de base correspondentes. É realmente significou para Content-negociação , de modo browser recebem a melhor alternativa entre os recursos disponíveis (como article.en.php, article.fr.php, article.jp.mp4).

  3. SetType ou SetHandler para .phpscripts sem extensão

    Uma abordagem mais direcionada para evitar carregar .phpsufixos em URLs é configurar o manipulador PHP para outros esquemas de arquivos. A opção mais simples é substituir o tipo MIME / manipulador padrão via .htaccess:

    DefaultType application/x-httpd-php
    

    Dessa forma, você pode renomear seu article.phpscript para apenas article(sem extensão), mas ainda assim processá-lo como script PHP.

    Agora isso pode ter implicações de segurança e desempenho, porque todos os arquivos sem extensão seriam canalizados através do PHP agora. Portanto, você pode definir alternativamente esse comportamento apenas para arquivos individuais:

    <Files article>
      SetHandler application/x-httpd-php
      # or SetType 
    </Files>
    

    Isso depende um pouco da configuração do servidor e do PHP SAPI usado. Alternativas comuns incluem ForceType application/x-httpd-phpou AddHandler php5-script.

    Novamente, observe que essas configurações se propagam de uma .htaccesspara subpastas. Você sempre deve desativar a execução de scripts ( SetHandler Nonee Options -Execou php_flag engine offetc.) para recursos estáticos e upload / diretórios etc.

  4. Outros esquemas de reescrita do Apache

    Entre suas muitas opções, o Apache fornece mod_aliasrecursos - que às vezes funcionam tão bem quanto mod_rewriteo RewriteRules. Observe que a maioria deles deve ser configurada em uma <VirtualHost>seção, mas não nos .htaccessarquivos de configuração por diretório .

    • ScriptAliasMatché principalmente para scripts CGI, mas também deve funcionar para PHP. Permite regexps como qualquer outro RewriteRule. Na verdade, talvez seja a opção mais robusta para configurar um controlador frontal abrangente.

    • E uma planície também Aliasajuda com alguns esquemas simples de reescrita.

    • Mesmo uma ErrorDocumentdiretiva simples pode ser usada para permitir que um script PHP manipule caminhos virtuais. Observe que essa é uma solução alternativa, no entanto, proíbe qualquer coisa, exceto solicitações GET, e inunda o error.log por definição.

    Veja http://httpd.apache.org/docs/2.2/urlmapping.html para obter mais dicas.

mario
fonte