Redirecionamentos em PHP: como fazer

Redirecionamentos em PHP: como fazer

Se você trabalha com PHP, mais cedo ou mais tarde vai precisar enviar o visitante de uma URL para outra: depois de salvar um formulário, quando uma página muda de endereço ou quando quer proteger uma área privada do seu site. Essa ação é chamada de redirecionamento ou redirect, e é uma das tarefas mais comuns no desenvolvimento web. Neste artigo mostramos como fazer um redirect em PHP da forma correta: o que é um redirecionamento, para que serve, como usar a header() com Location, qual é a diferença entre um 301 e um 302 e quais são os erros mais comuns que você deve evitar. Vamos direto ao ponto, com explicações claras e exemplos de código prontos para testar. Se você quer apenas a resposta curta: em PHP, um redirect é feito enviando o cabeçalho HTTP Location com a função header() e interrompendo a execução com exit. Mas, como em tudo que envolve HTTP, os detalhes importam: o momento em que a função é chamada, o código de status que a acompanha e o tratamento da saída do script fazem a diferença entre um redirecionamento que funciona e um incômodo aviso de headers already sent na tela.

O que é um redirecionamento em PHP

Para entender os redirecionamentos em PHP, primeiro é preciso lembrar como a web funciona. Quando o navegador solicita uma página, o servidor responde com uma mensagem HTTP composta de três partes: a linha de status com um código (por exemplo 200 OK), um conjunto de cabeçalhos ou headers e, opcionalmente, o corpo com o HTML. O PHP é o programa que roda no servidor e constrói essa resposta. Um redirecionamento nada mais é do que uma resposta HTTP especial: em vez de devolver o conteúdo da página, o servidor envia um cabeçalho Location com a URL para onde o navegador deve ir e um código de status que indica se a mudança é permanente ou temporária. O navegador recebe essa resposta, lê a nova URL e faz uma segunda requisição automaticamente. Para o usuário tudo parece instantâneo: ele digita um endereço e cai em outro. Essa é a forma correta e a que os mecanismos de busca usam. Existem outras técnicas para levar o usuário a outra página, como a tag meta de refresh do HTML ou o JavaScript com window.location, mas nenhuma substitui um redirecionamento HTTP real quando você precisa mover uma URL, preservar o ranqueamento ou proteger uma rota: o redirecionamento com PHP acontece no servidor, antes de enviar uma única linha de HTML.

Para que serve um redirect em PHP

Existem muitos cenários em que uma página precisa responder com um redirecionamento em PHP. Os mais comuns são:
  • A página mudou de endereço: se você reorganiza a estrutura do site, migra para outro domínio ou remove uma página, redireciona a URL antiga para a nova para que ninguém encontre um erro 404.
  • URLs amigáveis e canônicas: com redirecionamentos você unifica as variantes de uma mesma página, por exemplo de http para https, de www para sem www ou de index.php para a raiz, para que todo o tráfego e o SEO apontem para uma única URL.
  • Depois de enviar um formulário: quando um formulário salva dados, o correto é responder com um redirecionamento para uma página de confirmação em vez de HTML direto; assim, se o usuário atualizar a página, o navegador não reenvia o formulário nem duplica o pedido. É o padrão PRG que veremos adiante.
  • Redirect 301 para SEO: um 301 informa ao Google que a página mudou de forma definitiva e transfere a maior parte da autoridade da URL antiga para a nova.
  • Proteger o acesso: se uma página exige login ou um papel específico, o script redireciona o visitante para a tela de login quando a condição não é atendida.
Em todos esses casos o redirecionamento não é um enfeite: faz parte da arquitetura do site e afeta a experiência do usuário, a segurança e o posicionamento nos buscadores.

Como fazer um redirect em PHP com header e Location

A função fundamental é a header(), que envia um cabeçalho HTTP bruto. Para redirecionar, você envia o cabeçalho Location com a URL de destino. O exemplo mais simples de um redirect em PHP é este:
  • <?php
  • // Redireciona o navegador para outra URL
  • header("Location: https://www.exemplo.com/nova-pagina");
  • exit;
Simples assim. A função header() diz ao navegador: a página que você pediu agora mora nesta outra URL. Quando o PHP encontra um cabeçalho Location sem um código de status explícito, ele adiciona automaticamente um 302 Found, ou seja, um redirecionamento temporário. Sobre os códigos, falamos na próxima seção. O exit depois da header é indispensável. O cabeçalho é enviado quando o script termina ou quando a saída começa a fluir, mas o código que vem depois da header() continua executando se você não o interromper. Sem o exit, o script pode continuar processando dados, gravando no banco ou enviando mais cabeçalhos que interferem no redirecionamento. A boa prática é escrever exit logo depois de cada header com Location. Sobre a URL de destino: a especificação HTTP pede uma URL absoluta, do tipo https://www.exemplo.com/nova-pagina, e é a forma mais compatível e recomendada. Na prática, os navegadores modernos também aceitam um caminho absoluto do próprio site, como header("Location: /nova-pagina"), mas se você quer máxima compatibilidade, use a URL completa. Outra regra de ouro: a chamada à header() deve acontecer antes de o script produzir qualquer saída, seja HTML, um echo ou até um espaço em branco. Se o servidor já começou a enviar o corpo da resposta, os cabeçalhos não podem mais ser modificados e o PHP dispara o famoso aviso headers already sent. Mais adiante mostramos como evitar isso.

Redirect 301 vs 302: permanente ou temporário

Nem todos os redirecionamentos significam a mesma coisa. O código de status HTTP que acompanha o Location informa ao navegador e aos buscadores se a mudança é definitiva ou passageira, e isso tem consequências práticas:
Código Nome Quando usar
301 Moved Permanently A página mudou para sempre: os buscadores atualizam a URL, o SEO é preservado e os favoritos dos usuários são atualizados.
302 Found A mudança é temporária: manutenção, promoções ou mudanças de curta duração. Os buscadores mantêm a URL original.
303 See Other Depois de processar um formulário enviado com POST: o navegador faz uma requisição GET para a página de confirmação (padrão PRG).
307 Temporary Redirect Como o 302, mas preservando o método HTTP: se a requisição foi POST, a repetição também será POST.
O caso que mais interessa ao SEO é o 301. Quando uma página muda de forma permanente, o 301 Moved Permanently avisa ao Google que ele deve indexar a nova URL e transferir para lá o valor da antiga. Por isso ele é usado quando você troca de domínio, reorganiza a arquitetura do site ou unifica conteúdo duplicado. Já o 302 é para mudanças temporárias, como uma página de manutenção ou uma promoção de poucos dias: o buscador mantém a URL original nos resultados. Para fazer um redirect em PHP com código 301, passe o código como terceiro argumento da header():
  • header("Location: https://www.exemplo.com/nova-pagina", true, 301);
  • exit;
Você também pode definir primeiro o código com http_response_code() e depois enviar o cabeçalho:
  • http_response_code(301);
  • header("Location: https://www.exemplo.com/nova-pagina");
  • exit;
As duas formas são válidas; escolha a que ficar mais legível para você. Mas use o 301 com critério: navegadores e buscadores guardam em cache os redirecionamentos permanentes, então, se você tentar desfazer um depois, os usuários podem continuar vendo a versão antiga por um tempo. E evite redirecionar páginas soltas para a página inicial apenas para não mantê-las: muitas vezes um 404 limpo é melhor do que uma cadeia confusa de redirecionamentos.

Padrão PRG: redirecionar depois de enviar um formulário

Um dos usos mais valiosos do redirect em PHP é o padrão Post/Redirect/Get, conhecido como PRG. O problema que ele resolve é clássico: um formulário de pedido, cadastro ou contato envia seus dados com POST e o servidor os processa. Se o script responder imprimindo a página de confirmação, o navegador mantém a requisição POST no histórico; quando o usuário aperta F5 ou volta na página, o navegador pergunta se ele quer reenviar o formulário e, se ele aceitar, o pedido é processado duas vezes. A solução PRG é que o script que recebe o POST não devolva HTML: depois de validar e salvar os dados, ele responde com um redirecionamento, normalmente 303 See Other, para uma página acessada por GET. Assim, ao atualizar, apenas a página de confirmação é recarregada, sem efeitos colaterais:
  • <?php
  • // salvar_pedido.php: recebe o formulário via POST
  • if ($_SERVER["REQUEST_METHOD"] === "POST") {
  • // aqui você valida e salva o pedido
  • salvar_pedido($_POST);
  • // 303 para que a atualização não repita o envio
  • header("Location: /pedido-confirmado.php", true, 303);
  • exit;
  • }
O 303 See Other é o código pensado para esse caso: ele diz ao navegador para fazer uma requisição GET para a URL indicada, mesmo que a requisição original tenha sido um POST. Essa é a diferença entre o padrão PRG bem feito e o incômodo aviso de reenvio de formulário do navegador.

Redirect condicional: proteger páginas e escolher o destino

Um redirecionamento em PHP quase nunca é uma linha solta: normalmente ele depende de uma condição. O caso típico é proteger uma página privada. Se o usuário não fez login, ele é redirecionado para a tela de login antes de ver qualquer conteúdo:
  • <?php
  • session_start();
  • // se não há sessão ativa, o visitante vai para o login
  • if (!isset($_SESSION["usuario"])) {
  • header("Location: /login.php");
  • exit;
  • }
  • // o restante da página só executa com sessão ativa
A condição pode ser qualquer coisa: que o usuário tenha um papel específico, que o artigo exista no banco de dados ou que um parâmetro da URL tenha determinado valor. Quando o destino depende da requisição, redirecione contra uma lista branca de destinos permitidos em vez de usar o valor do usuário diretamente, para evitar os redirecionamentos abertos, uma vulnerabilidade que permite enganar visitantes para que caiam em sites externos:
  • <?php
  • // destinos permitidos
  • $secoes = array(
  • "perfil" => "/perfil.php",
  • "pedidos" => "/pedidos.php",
  • "ajuda" => "/ajuda.php"
  • );
  • $chave = $_GET["ir"] ?? "perfil";
  • if (isset($secoes[$chave])) {
  • header("Location: " . $secoes[$chave]);
  • exit;
  • }
  • // se a chave não existe, vai para a página inicial
  • header("Location: /inicio");
  • exit;
Nunca construa a URL do Location concatenando diretamente um valor de $_GET, $_POST ou $_SERVER sem validá-lo: se o destino depende da entrada do usuário, um atacante pode usar sua página como ponte para sites de phishing.

Redirecionar com atraso

Às vezes você não quer pular de imediato, mas mostrar uma mensagem e levar o usuário para a nova página alguns segundos depois. A forma simples é o cabeçalho Refresh, que, embora não faça parte do padrão HTTP, é compatível com todos os navegadores:
  • header("Refresh: 5; url=https://www.exemplo.com/nova-pagina");
  • echo "Você será redirecionado para a nova página em 5 segundos...";
  • exit;
Outra alternativa para atrasos curtos é pausar o script com sleep() antes de enviar o cabeçalho: o navegador espera e depois salta. Use com moderação, porque uma espera sem explicação prejudica a experiência. Na prática, para páginas de confirmação ou avisos, costuma ser melhor mostrar a mensagem com um link em vez de obrigar o usuário a esperar, ou redirecionar imediatamente com um 303.

Erros comuns ao fazer um redirect em PHP

Embora a mecânica seja simples, alguns erros se repetem nos projetos. Conhecê-los vai economizar horas de depuração. 1. O aviso headers already sent. É o erro mais pesquisado do desenvolvimento PHP e aparece quando você chama a header() depois de o script já ter enviado saída. Qualquer coisa conta: um echo, uma linha de HTML, um espaço ou uma quebra de linha fora das tags PHP, e até um BOM invisível de codificação UTF-8 que alguns editores adicionam no início do arquivo. Quando o corpo da resposta já começou a ser enviado, os cabeçalhos não podem ser modificados e o redirecionamento não acontece:
  • <?php
  • echo "Bem-vindo à loja";
  • // em um servidor sem buffer de saída, isso já envia HTML
  • header("Location: /inicio");
  • // Warning: Cannot modify header information - headers already sent
Para evitar:
  • Coloque a header() e sua lógica no início do script, antes de qualquer saída: estruture o código para decidir o redirecionamento antes de gerar o HTML.
  • Omita a tag de fechamento ?> no final dos arquivos que contêm apenas PHP: assim não sobram espaços ou quebras de linha que sejam enviados como saída.
  • Se não puder evitar a saída anterior, ative o buffer de saída com ob_start() no início do script; a saída fica retida e a header() pode executar depois.
  • Confira se o arquivo não foi salvo com BOM UTF-8, porque esses três bytes invisíveis no início também contam como saída.
  • Revise se não há espaços ou linhas em branco antes da tag de abertura <?php.
O buffer de saída resolve o problema quando você não consegue reordenar o código:
  • <?php
  • ob_start(); // a partir daqui, a saída fica no buffer
  • // ... qualquer echo ou HTML fica retido ...
  • header("Location: /inicio"); // sem conflito agora
  • exit;
2. Esquecer o exit depois da header(). Se o script continuar, o restante do código executa: ele pode enviar mais cabeçalhos, exibir conteúdo ou processar dados que não deveriam ser processados. Crie o hábito de escrever exit logo depois de cada redirecionamento. 3. Criar loops de redirecionamento. Se a URL de destino redireciona de volta para a original, o navegador entra em um ciclo e termina mostrando um erro de redirecionamentos demais. Isso acontece ao depurar rotas protegidas: confira se a página de login não redireciona para a página que redireciona para o login. Uma boa forma de ver a cadeia completa é testar com o curl no terminal, porque o curl -I mostra apenas os cabeçalhos da resposta:
  • curl -I https://www.exemplo.com/pagina-antiga
  • HTTP/1.1 301 Moved Permanently
  • Location: https://www.exemplo.com/nova-pagina
4. Usar 301 para mudanças que não são permanentes. Como os 301 ficam em cache no navegador e nos buscadores, um redirecionamento permanente feito por engano é difícil de reverter. Reserve o 301 para mudanças definitivas e use 302 ou 303 para o restante.

Em resumo

Fazer um redirect em PHP é simples, mas fazer bem exige entender HTTP. A regra que resume todo o artigo é: header("Location: URL") antes de qualquer saída, com o código de status adequado e seguida de exit.
  • header() com Location envia o navegador para outra URL e deve ser chamada antes de qualquer saída.
  • Sem um código explícito, o PHP responde com um 302 temporário; use o terceiro argumento da header() ou o http_response_code() para enviar 301, 303 e os demais códigos.
  • O padrão PRG responde a um POST com um redirect 303 para evitar duplicatas ao atualizar a página.
  • Use redirecionamentos condicionais para proteger páginas e sempre valide o destino contra uma lista branca.
  • Ao ver o aviso headers already sent, revise a saída anterior, remova o fechamento das tags PHP e teste com ob_start().
  • Confira seus redirecionamentos com curl -I para ver o código de status e a URL de destino reais.
Com esses fundamentos, você pode implementar redirecionamentos limpos, seguros e amigáveis ao SEO em qualquer projeto PHP, de um script pequeno a uma aplicação completa.
Chatea por WhatsApp