Durante remodelações estruturais de sítios web, alterações de taxonomia em lojas WooCommerce ou migrações de domínio, a integridade do tráfego orgânico depende exclusivamente de um mapeamento rigoroso de redirecionamentos. Em servidores com pilha web Apache ou LiteSpeed, o ficheiro .htaccess é o mecanismo primário de reescrita de rotas.
Contudo, a configuração empírica do .htaccess com fragmentos de código retirados de discussões online costuma originar problemas graves: cadeias de redirecionamentos com múltiplos saltos, parâmetros de rastreio perdidos e trabalho extra do servidor web em cada pedido. Este artigo apresenta uma abordagem estruturada para programadores que necessitam de implementar regras limpas, seguras e de elevado desempenho.
1. Como o bloco nativo do WordPress trata pedidos e redirecionamentos
Quando instala o WordPress e ativa a estrutura de ligações permanentes (Pretty Permalinks), o núcleo gera um bloco padrão no ficheiro .htaccess:
# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPressPorque é que as regras devem ficar antes do bloco WordPress
Se adicionar as suas regras personalizadas no final do ficheiro, após a linha # END WordPress, elas nunca serão executadas para páginas apagadas ou movidas.
A linha RewriteRule . /index.php [L] contém a flag [L] (Last). Quando um pedido para um endereço inexistente atinge esta instrução, o Apache entrega o pedido ao script index.php do WordPress e termina a passagem atual pelas regras. O WordPress assume então o pedido e gera uma página de erro 404, sem nunca consultar as regras situadas abaixo.
Regra mandatória: Qualquer regra de redirecionamento 301, reencaminhamento de domínio ou normalização canónica deve ser inserida estritamente antes do comentário # BEGIN WordPress.
Além disso, sempre que altera as definições de ligações permanentes no painel de administração, o WordPress reescreve por completo o conteúdo situado entre # BEGIN WordPress e # END WordPress, apagando qualquer instrução que tenha sido colocada inadvertidamente no interior desse espaço.
2. Incompatibilidades entre mod_alias e mod_rewrite
O Apache dispõe de dois módulos para manipular caminhos de rede:
mod_alias: Controla as diretivasRedirecteRedirectMatch. É direto e eficiente para correspondências estáticas, mas compara apenas o caminho do URL e não tem acesso a variáveis avançadas do servidor nem a cadeias de consulta (query strings).mod_rewrite: O motor completo de reescrita com suporte para expressões regulares, condições lógicas (RewriteCond) e flags de controlo de fluxo.
Ordem de processamento entre mod_rewrite e mod_alias
Os dois módulos podem corresponder ao mesmo caminho. Qual deles atua primeiro é decidido pela forma como o Apache processa os módulos, não pela ordem visual das linhas no ficheiro .htaccess.
Considere o seguinte exemplo:
# Exemplo incorreto:
Redirect 301 /servico-antigo/ /servico-novo/
RewriteRule ^servico-antigo/(.*)$ /catalogo/$1 [R=301,L]As duas linhas reclamam o mesmo endereço e enviam-no para destinos diferentes. Ler o ficheiro de cima para baixo não diz qual das respostas o visitante vai receber, e uma alteração posterior numa delas pode criar um ciclo de redirecionamentos.
Boa prática de engenharia: Use um só módulo para cada URL. Em projetos WordPress, onde o motor de reescrita já está ativo, manter todos os redirecionamentos em RewriteRule dá coerência ao ficheiro; para casos simples, a própria documentação do Apache prefere Redirect, como se explica mais abaixo.
3. Como redirecionar páginas movidas 1:1 no .htaccess
Para páginas individuais que mudaram de endereço de forma permanente, deve isolar o início (^) e o fim ($) do caminho relativo com expressões regulares:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Redirecionar página única mantendo compatibilidade com barra final
RewriteRule ^sobre-nos/?$ /empresa/ [R=301,L]
# Redirecionar ficheiro com extensão legada para URL limpo
RewriteRule ^artigo-tecnico\.html$ /artigo-tecnico/ [R=301,L]
</IfModule>A inclusão do padrão /? torna a regra insensível à presença ou ausência da barra final no pedido do utilizador. A flag [R=301] instrui o servidor a emitir o código de estado HTTP 301 Moved Permanently, que a Google usa como sinal de que o novo endereço deve ser o canónico.
4. Como normalizar HTTPS e WWW no .htaccess
Um dos problemas mais comuns detetados em auditorias técnicas é a existência de cadeias com múltiplos redirecionamentos 301 sucessivos. Por exemplo:
http://dominio.ptreencaminha parahttps://dominio.pthttps://dominio.ptreencaminha parahttps://www.dominio.pt
Cada salto adicional acrescenta latência para o utilizador, como lembra o guia da Google sobre mudanças de sítio. Deve consolidar as duas verificações numa única avaliação lógica:
Forçar HTTPS e prefixo www numa só regra
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Se não estiver em HTTPS OU se o domínio não começar por www
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^(.*)$ https://www.%1/$1 [R=301,L]
</IfModule>Forçar HTTPS e domínio sem www (naked domain)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Se não estiver em HTTPS OU se o domínio contiver o prefixo www
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
</IfModule>Em qualquer dos cenários, qualquer combinação de protocolo inseguro ou formato de anfitrião é resolvida num único salto direto para o endereço canónico final.
5. Como gerir query strings em redirecionamentos .htaccess
A diretiva padrão RewriteRule avalia apenas o caminho textual do pedido (Request Path), ignorando os argumentos presentes após o ponto de interrogação (?).
Isto significa que a regra seguinte não funcionará:
# REGRA INEFICAZ:
RewriteRule ^detalhe\.php\?produto_id=40$ /novo-produto/ [R=301,L]Para capturar e redirecionar endereços antigos que dependiam de parâmetros GET (típicos de plataformas legadas), é obrigatório recorrer à variável %{QUERY_STRING} através de RewriteCond:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Redirecionar URL antigo com parâmetro específico e descartar a query string
RewriteCond %{QUERY_STRING} (^|&)produto_id=40(&|$)
RewriteRule ^detalhe\.php$ /novo-produto/ [QSD,R=301,L]
</IfModule>Os delimitadores (^|&) e (&|$) impedem que a condição apanhe também produto_id=400 ou outro_produto_id=40.
Para que serve a flag QSD no .htaccess
A flag QSD existe desde o Apache 2.4.0 e garante que os parâmetros antigos são eliminados no destino. Sem esta flag, o Apache preserva a cadeia original e produz o endereço indesejado /novo-produto/?produto_id=40. Em configurações mais antigas, o mesmo efeito obtinha-se com um ponto de interrogação no fim do destino (/novo-produto/?), como indica a documentação das flags.
Caso pretenda preservar parâmetros legítimos de campanha ou paginação, utilize a flag QSA (Query String Append):
# Preservar parâmetros UTM ou filtros na mudança de taxonomia
RewriteRule ^antiga-categoria/(.*)$ /nova-categoria/$1 [QSA,R=301,L]6. Como fazer redirecionamento integral de domínio no .htaccess
Quando duas marcas se fundem ou um projeto transita para um novo domínio com preservação da estrutura interna de páginas, implementa-se o reencaminhamento global:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Encaminhar todo o tráfego de dominio-antigo.pt para dominio-novo.pt
RewriteCond %{HTTP_HOST} ^(?:www\.)?dominio-antigo\.pt$ [NC]
RewriteRule ^(.*)$ https://dominio-novo.pt/$1 [R=301,L]
</IfModule>A variável $1 armazena a totalidade do caminho relativo após a barra inicial, mantendo intactas as referências a artigos, imagens e documentos.
7. Quando mover redirecionamentos para fora do .htaccess
Embora o ficheiro .htaccess ofereça flexibilidade durante o desenvolvimento local, o seu funcionamento em produção acarreta um custo contínuo:
- A cada pedido de imagem, ficheiro de estilos ou fonte tipográfica, o servidor tem de verificar o sistema de ficheiros em busca de ficheiros
.htaccessem todas as diretorias superiores. O tutorial do Apache dá o exemplo de um ficheiro em/www/htdocs/example, que obriga a quatro consultas adicionais ao sistema de ficheiros. - O ficheiro é carregado de novo cada vez que um documento é pedido, por isso uma lista com centenas ou milhares de linhas é lida repetidamente.
Alternativas ao .htaccess: VirtualHost, Nginx e CDN
- Configuração no VirtualHost: Se dispõe de acesso ao ficheiro de configuração do Apache (
httpd.confou ficheiros emsites-available), transfira as listas volumosas para o bloco do anfitrião virtual. A configuração principal é carregada uma vez no arranque, em vez de ser lida a cada pedido. - Mapeamento em Nginx: Em arquiteturas com Nginx como proxy inverso diante do Apache, estruture as listas através de blocos
mapna configuração do Nginx. - Redirecionamentos na CDN (Edge Redirects): Para sítios de comércio eletrónico de grande escala com milhares de URLs descontinuados, descarregue essa responsabilidade para a Cloudflare (Bulk Redirects). O pedido é respondido na borda da rede e nunca chega ao servidor de origem.
Como testar e validar redirecionamentos .htaccess
Antes de disponibilizar novas regras em ambiente de produção, cumpra as seguintes etapas de segurança:
- Confirme que todas as instruções residem antes da linha
# BEGIN WordPress. - Verifique o código de estado e o destino de cada salto na linha de comandos:
curl -ILs "https://dominio.pt/url-antigo/" | grep -E "HTTP|Location". - Teste em navegadores anónimos ou com as ferramentas de desenvolvimento abertas com a opção Disable cache ativa, para não sofrer a interferência de respostas 301 gravadas localmente.
Como resolver ciclos de redirecionamento no .htaccess
O que provoca um ciclo de redirecionamento
- Referências circulares: a regra A redireciona para B e a regra B devolve o pedido para A.
- Conflito de protocolo: uma regra para HTTP choca com a regra que força HTTPS.
- Conflito de WWW: as regras com www e sem www anulam-se uma à outra.
- Conflito com o WordPress: os redirecionamentos de um plugin chocam com os do
.htaccess.
Registar o mod_rewrite para encontrar o ciclo
# Configuração do servidor ou do anfitrião virtual: LogLevel não é permitido no .htaccess
LogLevel alert rewrite:trace3As diretivas RewriteLog e RewriteLogLevel foram removidas no Apache 2.4 e substituídas pela definição LogLevel por módulo (mod_rewrite). A mesma página avisa que níveis acima de trace2 tornam o servidor muito mais lento, por isso use-os só para depuração e remova a linha no fim. Para ver apenas as entradas de reescrita: tail -f error_log | fgrep '[rewrite:'.
Como parar um ciclo de redirecionamento no .htaccess
# Evitar ciclos verificando se o pedido já foi redirecionado
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
# Tratamento explícito do protocolo para evitar ciclos
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]Como a Google trata os redirecionamentos 301, 302, 307 e 308
A documentação da Google sobre redirecionamentos (Redirecionamentos e a Pesquisa Google) divide-os em dois grupos. Os redirecionamentos permanentes (301 e 308) são seguidos pelo Googlebot, e o processo de indexação usa-os como sinal de que o destino deve ser o canónico, pelo que é o novo URL que aparece nos resultados. Os temporários (302, 303 e 307) também são seguidos, mas não contam como sinal canónico, e o URL de origem costuma manter-se nos resultados. A Google ordena ainda os métodos pela fiabilidade com que os consegue interpretar: um redirecionamento do lado do servidor, como o do .htaccess, vem em primeiro lugar, à frente do meta refresh e do JavaScript.
Isto conta no .htaccess porque os dois módulos do Apache usam o código temporário por omissão. Redirect sem argumento de estado envia um 302 (mod_alias), e RewriteRule com a flag [R] simples também envia um 302 (flags do RewriteRule). Uma migração escrita com [R,L] em vez de [R=301,L] publica redirecionamentos temporários sem qualquer mensagem de erro.
As cadeias também têm limite. Os rastreadores da Google seguem até 10 saltos de redirecionamento por omissão (códigos de estado HTTP), e o guia de mudança de sítio pede, idealmente, não mais de 3 saltos e menos de 5 no máximo, porque cada salto acrescenta latência (mudanças de sítio com alteração de URL). O mesmo guia recomenda manter os redirecionamentos de uma migração durante o máximo de tempo possível, em geral pelo menos um ano.
Porque é que um RewriteRule com barra inicial não funciona no .htaccess
A razão mais comum para uma regra copiada da configuração do servidor não fazer nada no .htaccess é a barra inicial. Em contexto por diretoria (.htaccess e <Directory>), o Apache retira o caminho da diretoria, até à barra final inclusive, antes de comparar o padrão. A documentação do mod_rewrite di-lo diretamente: a cadeia nunca tem barra inicial, por isso um padrão que começa por ^/ nunca corresponde em contexto por diretoria.
# Configuração do VirtualHost: corresponde a /pagina-antiga/
RewriteRule ^/pagina-antiga/$ /pagina-nova/ [R=301,L]
# .htaccess na raiz do documento: o mesmo redirecionamento
RewriteRule ^pagina-antiga/$ /pagina-nova/ [R=301,L]O padrão também nunca vê o nome do anfitrião nem a query string. Para os testar, use um RewriteCond sobre %{HTTP_HOST} ou %{QUERY_STRING}, e recorra a %{REQUEST_URI} quando precisar do caminho completo. Duas definições do servidor decidem se as regras chegam a correr: a reescrita no .htaccess exige Options FollowSymLinks ou SymLinksIfOwnerMatch, e tanto RewriteRule como Redirect precisam de AllowOverride FileInfo. O tutorial do .htaccess do Apache sugere um teste rápido: escreva uma palavra sem sentido, como TestMe, no ficheiro e recarregue a página. Se não aparecer um erro 500, está em vigor AllowOverride None e o Apache ignora o ficheiro.
Diferença entre as flags L e END no .htaccess
No .htaccess, [L] não significa “parar de vez”. A documentação das flags explica que, em contexto por diretoria, [L] termina a passagem atual pelas regras, mas o pedido reescrito pode ser processado de novo desde o início, o que pode causar ciclos. [END] interrompe todo o processamento de reescrita seguinte para esse pedido, e a documentação apresenta-a como a flag preferível para a maioria das regras por diretoria. Em contexto de servidor ou de anfitrião virtual, as duas comportam-se da mesma forma.
Três pormenores da mesma página explicam muitos redirecionamentos avariados:
[R]sem[L]passa o URL reescrito à regra seguinte, o que pode gerar avisosInvalid URI in requestno registo de erros, por isso escreva[R=301,L].- Com um código fora do intervalo 300 a 399, por exemplo
[R=410], a substituição é ignorada e o URL de destino nunca é enviado.[G]e[F]exprimem essa intenção com mais clareza. [QSD](Apache 2.4.0 e posterior) descarta a query string e, quando[QSD]e[QSA]aparecem juntas, prevalece[QSD].
Quando o Apache recomenda Redirect em vez de RewriteRule
O guia da própria equipa do Apache, Quando não usar o mod_rewrite, chama ao mod_rewrite um último recurso e diz que redirecionamentos simples de um URL ou de uma classe de URLs devem usar Redirect ou RedirectMatch. Redirect mantém a informação de caminho, por isso Redirect "/one/" "http://one.example.com/" também move /one/two.html. Só corresponde a segmentos de caminho completos: /service não apanha /servicefoo.txt. Passa os parâmetros GET para o destino, e os corpos de pedidos POST são descartados (mod_alias).
Para um nome de anfitrião canónico, o mesmo guia usa um bloco <If> em vez de RewriteCond:
<If "req('Host') != 'www.example.com'">
Redirect "/" "https://www.example.com/"
</If>Refere também que o padrão de front-controller que o WordPress escreve no .htaccess (encaminhar todos os ficheiros e diretorias inexistentes para index.php) pode ser expresso com FallbackResource /index.php, que também funciona no .htaccess. Numa instalação WordPress, deixe o bloco gerado como está, porque o WordPress reescreve tudo entre # BEGIN WordPress e # END WordPress. A regra prática é manter um módulo por URL: não deixe que um Redirect e um RewriteRule correspondam ao mesmo caminho.
Como usar RewriteMap para guardar milhares de redirecionamentos
Uma lista longa de redirecionamentos no .htaccess paga-se em cada pedido. Segundo o tutorial do .htaccess, quando os ficheiros .htaccess estão permitidos, o httpd procura-os em todas as diretorias e diretorias superiores e volta a carregar o ficheiro sempre que um documento é pedido, incluindo imagens, CSS e JavaScript. A mesma página recomenda a configuração principal do servidor sempre que tiver acesso a ela.
A resposta do Apache para mapeamentos grandes é o RewriteMap. Só pode ser declarado em contexto de servidor ou de anfitrião virtual, nunca no .htaccess (guia do RewriteMap). Um mapa em texto simples tem um par chave e valor por linha, separados por espaço, e o httpd guarda em cache as chaves consultadas até a data de modificação do ficheiro mudar. Um mapa dbm: criado com httxt2dbm é indexado, por isso as consultas são mais rápidas do que num ficheiro de texto:
# Configuração do VirtualHost, não .htaccess
RewriteEngine On
RewriteMap redirects "dbm:/etc/apache2/redirects.map"
RewriteCond "${redirects:%{REQUEST_URI}}" !=""
RewriteRule "^" "${redirects:%{REQUEST_URI}}" [R=301,L]# redirects.txt: um par "caminho-antigo caminho-novo" por linha
# /pagina-antiga/ /pagina-nova/
httxt2dbm -i redirects.txt -o redirects.mapA condição ignora o redirecionamento quando o caminho não está no mapa, por isso os URLs sem correspondência seguem para o WordPress como de costume.
Se necessita de apoio profissional na orquestração de migrações complexas de catálogos WooCommerce ou otimização profunda de infraestrutura, conheça o nosso trabalho de consultoria e engenharia WordPress.







