Programação Por Equipe DevBras

Capa do artigo: Redirecionar URL: 301 para a busca, e nunca para um endereço que o usuário mandou

Redirecionar URL: 301 para a busca, e nunca para um endereço que o usuário mandou

Escolha entre 301, 302, 307 e 308, configure o redirecionamento no Apache, no nginx ou no PHP, evite cadeias e valide o parâmetro de retorno para não virar porta de golpe.

Você reorganizou o site, a página nova está no ar e a antiga ainda aparece na busca. Ou o login manda a pessoa de volta para onde ela estava usando um ?next= na URL. A resposta curta: para endereço que mudou de vez, responda no servidor com 301, ou 308 se o método precisa ser preservado, apontando direto para o destino final. Para mudança temporária, use 302 ou 307. E nunca redirecione para um destino vindo da query sem validar: aceite só caminho interno do seu próprio domínio, e mande para a home todo o resto.

Sem redirecionamento, você tem duas URLs e a antiga perde força sozinha, devagar e mal. Com o redirecionamento aberto, um link com o seu domínio leva a pessoa para um site de golpe que você não controla.

O que cada código de redirecionamento diz

Todo redirecionamento HTTP é uma resposta da faixa 3xx com o cabeçalho Location. O navegador lê o código, segue o endereço e, dependendo do código, guarda a decisão. A diferença entre eles está em duas perguntas: a mudança é permanente, e o método do pedido original deve ser mantido?

  • 301 Moved Permanently: mudou de vez. Navegadores podem guardar em cache, e buscadores passam a tratar o endereço novo como o oficial.
  • 308 Permanent Redirect: mesmo sentido do 301, com a garantia de que um POST continua POST no destino.
  • 302 Found: temporário. O endereço antigo continua sendo o oficial.
  • 307 Temporary Redirect: temporário, preservando método e corpo.
  • 303 See Other: depois de enviar um formulário, manda o navegador buscar a página de resultado com GET, o que evita reenviar o formulário ao atualizar a página.

Para páginas comuns, acessadas com GET, 301 e 308 têm o mesmo efeito prático, e 301 é o mais usado. O meta refresh no HTML e o redirecionamento por JavaScript existem, mas são o último recurso: só acontecem depois que a página carregou, deixam um histórico confuso no botão voltar e mandam um sinal mais fraco para a busca do que uma resposta 301 do servidor. O significado de cada faixa de status está em códigos de status HTTP.

Como redirecionar uma página que mudou de endereço

  1. Monte a lista de pares: URL antiga e URL nova. Exporte as antigas do sitemap ou dos relatórios de busca, não só da memória.
  2. Aponte cada antiga para o destino final mais próximo em conteúdo, não para a home.
  3. Configure o redirecionamento no servidor web ou na aplicação, com 301.
  4. Elimine cadeias: se A ia para B e B agora vai para C, faça A ir direto para C.
  5. Teste cada URL antiga com curl, olhando o status e o Location.
  6. Atualize os links internos do site para o endereço novo. O redirecionamento é para quem vem de fora.

No Apache

Redirect permanent /servicos/hospedagem.html /hospedagem/
RedirectMatch 301 ^/blog/([0-9]+)/(.*)$ /artigos/$2

A diretiva Redirect compara o começo do caminho e preserva o restante, o que surpreende: Redirect permanent /loja /produtos também manda /loja/camisa para /produtos/camisa. Quando você quer casar o caminho exato, use RedirectMatch com ^ e $.

No nginx

location = /servicos/hospedagem.html {
    return 301 /hospedagem/;
}

server {
    listen 80;
    server_name exemplo.com.br www.exemplo.com.br;
    return 301 https://exemplo.com.br$request_uri;
}

O segundo bloco faz HTTP e www irem para o endereço oficial num salto só, em vez de HTTP ir para HTTPS com www e depois para HTTPS sem www. Como o nginx escolhe esses blocos pelo nome está em nomes de servidor no nginx.

No PHP

<?php
header('Location: /hospedagem/', true, 301);
exit;

O exit não é opcional. Sem ele, o resto do script continua executando depois do cabeçalho, e o que vier em seguida pode gravar dado ou vazar conteúdo para quem ignora o redirecionamento.

Como validar o destino que vem da URL

O padrão "volte para onde estava" depois do login é útil e perigoso. Se o código faz header('Location: ' . $_GET['next']) sem filtro, alguém monta https://seusite.com.br/login?next=https://golpe.exemplo. O link tem o seu domínio, passa pelo filtro de quem desconfia de link estranho, e entrega a pessoa numa cópia da sua página de login. Isso se chama redirecionamento aberto.

Checar só se o texto começa com barra não basta. Navegadores tratam //golpe.exemplo como endereço de outro domínio, e também interpretam a barra invertida em /\golpe.exemplo do mesmo jeito. O caminho mais seguro é deixar um parser de URL resolver o endereço e comparar a origem:

const BASE = 'https://www.exemplo.com.br';

function destinoSeguro(next) {
  if (typeof next !== 'string' || next === '') return '/';
  try {
    const url = new URL(next, BASE);
    if (url.origin !== new URL(BASE).origin) return '/';
    return url.pathname + url.search + url.hash;
  } catch {
    return '/';
  }
}

destinoSeguro('/minha-conta?aba=pedidos'); // '/minha-conta?aba=pedidos'
destinoSeguro('https://golpe.exemplo/');   // '/'
destinoSeguro('//golpe.exemplo/');         // '/'
destinoSeguro('/\\golpe.exemplo/');        // '/'
destinoSeguro('javascript:alert(1)');      // '/'

O URL do JavaScript segue o mesmo padrão de interpretação dos navegadores, então ele resolve as barras duplas e invertidas do mesmo jeito que o navegador resolveria. A função devolve só o caminho, nunca o endereço completo. Se você conhece a lista de destinos possíveis, uma lista fixa de caminhos permitidos é ainda melhor. O tema completo, com outros vetores, está em URL aberta não é redirecionar.

Como conferir o resultado

curl -sI https://exemplo.com.br/servicos/hospedagem.html
curl -sIL http://www.exemplo.com.br/servicos/hospedagem.html

O primeiro mostra o status e o Location de um salto. O segundo segue todos os saltos e mostra cada resposta no caminho. O ideal é um salto e um 200 no fim. 200 na URL velha significa que o redirecionamento não existe. Três saltos significam uma cadeia para encurtar.

Erros comuns

  • 302 numa mudança definitiva. A busca continua considerando a URL antiga como a oficial, e a nova demora a ganhar posição.
  • Mandar todas as URLs antigas para a home. A pessoa que procurava um produto cai na página inicial e sai, e a busca costuma tratar isso como página inexistente.
  • Cadeias de redirecionamento. Cada salto custa uma ida e volta na rede, e cadeias longas podem nem ser seguidas por robôs.
  • Testar 301 só no navegador. O navegador guarda o 301 em cache. Você corrige a regra e continua vendo o destino errado. Teste com curl, e teste regras novas com 302 antes de trocar para 301.
  • Redirecionar para o valor de ?next= sem validar. O seu domínio vira trampolim para phishing.
  • Esquecer o exit depois do header no PHP. O script continua rodando e pode executar ações que deviam estar protegidas.

Checklist

  • Existe uma lista de URLs antigas com o destino de cada uma.
  • Mudanças definitivas usam 301 ou 308, e temporárias usam 302 ou 307.
  • Cada URL antiga chega ao destino final em um salto.
  • HTTP, HTTPS, com e sem www convergem para um único endereço oficial.
  • Os links internos apontam direto para os endereços novos.
  • Todo destino vindo da query passa por validação de origem.
  • O curl com -sIL termina em 200 para cada URL antiga.

Pegue três URLs antigas do seu site e rode o curl com -sIL. Quantos saltos cada uma dá até chegar, e alguma ainda responde 200 sozinha?

Avalie este artigo

Referência: http://hostnet.com.br/wiki/index.php/Scripts_para_Redirecionamento_de_URL