Redirecciones en PHP: cómo hacer un redirect

Redirecciones en PHP: cómo hacer un redirect

Si trabajas con PHP, tarde o temprano vas a necesitar enviar al visitante de una URL a otra: después de guardar un formulario, cuando una página cambia de dirección o cuando quieres proteger una sección privada de tu sitio. A esa acción se le llama redirección o redirect, y es una de las tareas más habituales del desarrollo web. En este artículo te mostramos cómo hacer un redirect en PHP de forma correcta: qué es una redirección, para qué sirve, cómo se usa header() con Location, qué diferencia hay entre un 301 y un 302 y cuáles son los errores más comunes que debes evitar. Vamos directo al grano, con explicaciones claras y ejemplos de código listos para probar. Si buscas la respuesta corta: en PHP un redirect se hace enviando la cabecera HTTP Location con la función header() y deteniendo la ejecución con exit. Pero como en todo lo relacionado con HTTP, los detalles importan: el momento en que se llama la función, el código de estado que la acompaña y el manejo de la salida del script marcan la diferencia entre una redirección que funciona y un molesto aviso de headers already sent en pantalla.

Qué es una redirección en PHP

Para entender las redirecciones en PHP primero hay que recordar cómo funciona la web. Cuando el navegador pide una página, el servidor responde con un mensaje HTTP que tiene tres partes: la línea de estado con un código (por ejemplo 200 OK), un conjunto de cabeceras o headers y, opcionalmente, el cuerpo con el HTML. PHP es el programa que corre en el servidor y construye esa respuesta. Una redirección no es más que una respuesta HTTP especial: el servidor, en lugar de devolver el contenido de la página, envía una cabecera Location con la URL a la que debe ir el navegador y un código de estado que indica si el movimiento es permanente o temporal. El navegador recibe esa respuesta, lee la URL nueva y lanza una segunda petición de forma automática. Para el usuario todo parece instantáneo: escribe una dirección y aterriza en otra. Esa es la forma correcta y la que usan los buscadores. Existen otras técnicas para llevar al usuario a otra página, como la etiqueta meta de refresco de HTML o el JavaScript con window.location, pero ninguna sustituye a una redirección HTTP real cuando lo que necesitas es mover una URL, conservar el posicionamiento o proteger una ruta: la redirección con PHP ocurre en el servidor, antes de enviar una sola línea de HTML.

Para qué sirve un redirect en PHP

Hay muchos escenarios en los que una página necesita responder con una redirección en PHP. Estos son los más habituales:
  • La página cambió de dirección: si reorganizas la estructura del sitio, migras a otro dominio o eliminas una página, rediriges la URL antigua a la nueva para que nadie se encuentre con un error 404.
  • URLs amigables y canónicas: con redirecciones unificas las variantes de una misma página, por ejemplo de http a https, de www a sin www o de index.php a la raíz, para que todo el tráfico y el SEO apunten a una sola URL.
  • Después de enviar un formulario: cuando un formulario guarda datos, lo correcto es responder con una redirección a una página de confirmación y no con HTML directamente; así, si el usuario refresca, el navegador no reenvía el formulario ni duplica el pedido. Es el patrón PRG que veremos más adelante.
  • Redirección 301 para SEO: un 301 le indica a Google que la página se movió de forma definitiva y transmite la mayor parte de la autoridad de la URL antigua a la nueva.
  • Proteger el acceso: si una página requiere haber iniciado sesión o tener un rol concreto, el script redirige al visitante al login cuando no cumple la condición.
En todos estos casos la redirección no es un adorno: es parte de la arquitectura del sitio y afecta a la experiencia del usuario, a la seguridad y al posicionamiento en buscadores.

Cómo hacer un redirect en PHP con header y Location

La función clave es header(), que envía una cabecera HTTP sin procesar. Para redirigir se envía la cabecera Location con la URL de destino. El ejemplo más simple de un redirect en PHP es este:
  • <?php
  • // Redirige al navegador a otra URL
  • header("Location: https://www.ejemplo.com/nueva-pagina");
  • exit;
Así de corto. La función header() le dice al navegador: la página que pediste ahora vive en esta otra URL. Cuando PHP encuentra una cabecera Location sin un código de estado explícito, añade automáticamente un 302 Found, es decir, una redirección temporal. Sobre los códigos hablamos en la siguiente sección. El exit después de header es imprescindible. La cabecera se envía cuando el script termina o cuando la salida empieza a fluir, pero el código que venga después de header() se sigue ejecutando si no lo detienes. Sin exit, el script podría continuar procesando datos, escribir en la base de datos o enviar más cabeceras que interfieran con la redirección. La buena práctica es escribir exit justo después de cada header con Location. Sobre la URL de destino: la especificación HTTP pide una URL absoluta, del tipo https://www.ejemplo.com/nueva-pagina, y es la forma más compatible y recomendada. En la práctica los navegadores modernos también aceptan una ruta absoluta del propio sitio, como header("Location: /nueva-pagina"), pero si quieres máxima compatibilidad, usa la URL completa. Otra regla de oro: la llamada a header() debe ocurrir antes de que el script produzca cualquier salida, ya sea HTML, un echo o incluso un espacio en blanco. Si el servidor ya empezó a enviar el cuerpo de la respuesta, las cabeceras no se pueden modificar y PHP lanza el famoso aviso headers already sent. Más abajo vemos cómo evitarlo.

Redirección 301 vs 302: permanente o temporal

No todas las redirecciones significan lo mismo. El código de estado HTTP que acompaña a Location le dice al navegador y a los buscadores si el movimiento es definitivo o pasajero, y eso tiene consecuencias prácticas:
Código Nombre Cuándo usarlo
301 Moved Permanently La página se movió para siempre: cambia la URL en los buscadores, conserva el SEO y actualiza los marcadores de los usuarios.
302 Found El movimiento es temporal: mantenimiento, promociones o traslados de corta duración. Los buscadores mantienen la URL original.
303 See Other Después de procesar un formulario enviado con POST: el navegador hace una petición GET a la página de confirmación (patrón PRG).
307 Temporary Redirect Como el 302 pero conservando el método HTTP: si la petición fue POST, la repetición también será POST.
El caso que más interesa al SEO es el 301. Cuando una página se muda de forma permanente, el 301 Moved Permanently le avisa a Google de que debe indexar la nueva URL y trasladar allí el valor de la antigua. Por eso se usa cuando cambias de dominio, reorganizas la arquitectura del sitio o unificas contenido duplicado. El 302, en cambio, es para cambios temporales, como una página de mantenimiento o una promoción de pocos días: el buscador mantiene la URL original en sus resultados. Para lanzar un redirect en PHP con código 301 se pasa el código como tercer argumento de header():
  • header("Location: https://www.ejemplo.com/nueva-pagina", true, 301);
  • exit;
También puedes fijar primero el código con http_response_code() y después enviar la cabecera:
  • http_response_code(301);
  • header("Location: https://www.ejemplo.com/nueva-pagina");
  • exit;
Las dos formas son válidas; elige la que te resulte más legible. Eso sí, usa el 301 con criterio: los navegadores y los buscadores guardan en caché las redirecciones permanentes, así que si más tarde intentas deshacerla, los usuarios podrían seguir viendo la versión antigua durante un tiempo. Y evita redirigir páginas sueltas a la portada solo para no mantenerlas: muchas veces es mejor un 404 limpio que una cadena de redirecciones confusa.

Patrón PRG: redirigir después de enviar un formulario

Uno de los usos más valiosos del redirect en PHP es el patrón Post/Redirect/Get, conocido como PRG. El problema que resuelve es clásico: un formulario de pedido, registro o contacto envía sus datos con POST y el servidor los procesa. Si el script responde imprimiendo la página de confirmación, el navegador conserva la petición POST en el historial; cuando el usuario pulsa F5 o vuelve hacia atrás, el navegador pregunta si quiere reenviar el formulario y, si acepta, el pedido se procesa dos veces. La solución PRG consiste en que el script que recibe el POST no devuelva HTML: después de validar y guardar los datos, responde con una redirección, normalmente 303 See Other, hacia una página a la que se llega con GET. Así, al refrescar solo se vuelve a cargar la página de confirmación, sin efectos secundarios:
  • <?php
  • // guardar_pedido.php: recibe el formulario por POST
  • if ($_SERVER["REQUEST_METHOD"] === "POST") {
  • // aquí se valida y se guarda el pedido
  • guardar_pedido($_POST);
  • // 303 para que el refresco no repita el envío
  • header("Location: /pedido-confirmado.php", true, 303);
  • exit;
  • }
El 303 See Other es el código pensado para este caso: le indica al navegador que haga una petición GET a la URL indicada, aunque la petición original fuera un POST. Esa es la diferencia entre el patrón PRG bien hecho y el molesto mensaje de confirmación de reenvío del navegador.

Redirección condicional: proteger páginas y decidir el destino

La redirección en PHP casi nunca es una línea suelta: normalmente depende de una condición. El caso típico es proteger una página privada. Si el usuario no ha iniciado sesión, se le redirige al login antes de que vea cualquier contenido:
  • <?php
  • session_start();
  • // si no hay sesión iniciada, el visitante va al login
  • if (!isset($_SESSION["usuario"])) {
  • header("Location: /login.php");
  • exit;
  • }
  • // el resto de la página solo se ejecuta con sesión activa
La condición puede ser cualquier cosa: que el usuario tenga un rol determinado, que el artículo exista en la base de datos o que un parámetro de la URL tenga cierto valor. Cuando el destino depende de la petición, conviene redirigir contra una lista blanca de destinos permitidos en lugar de usar el valor del usuario directamente, para evitar las redirecciones abiertas, una vulnerabilidad que permite engañar a los visitantes para que terminen en sitios externos:
  • <?php
  • // destinos permitidos
  • $secciones = array(
  • "perfil" => "/perfil.php",
  • "pedidos" => "/pedidos.php",
  • "ayuda" => "/ayuda.php"
  • );
  • $clave = $_GET["ir"] ?? "perfil";
  • if (isset($secciones[$clave])) {
  • header("Location: " . $secciones[$clave]);
  • exit;
  • }
  • // si la clave no existe, se va a la portada
  • header("Location: /inicio");
  • exit;
Nunca construyas la URL de Location concatenando directamente un valor de $_GET, $_POST o $_SERVER sin validarlo: si el destino depende de la entrada del usuario, un atacante podría usar tu página como puente hacia sitios de phishing.

Redirigir con retardo

A veces no quieres saltar de inmediato, sino mostrar un mensaje y llevar al usuario a la nueva página después de unos segundos. La forma sencilla es la cabecera Refresh, que aunque no forma parte del estándar HTTP, es compatible con todos los navegadores:
  • header("Refresh: 5; url=https://www.ejemplo.com/nueva-pagina");
  • echo "Te redirigimos a la nueva página en 5 segundos...";
  • exit;
Otra alternativa cuando el retardo es corto es pausar el script con sleep() antes de enviar la cabecera: el navegador espera y luego salta. Úsalo con moderación, porque una espera sin explicación empeora la experiencia. En la práctica, para páginas de confirmación suele ser mejor mostrar el mensaje con un enlace y no obligar al usuario a esperar, o redirigir de inmediato con un 303.

Errores comunes al hacer un redirect en PHP

Aunque la mecánica es sencilla, hay varios errores que se repiten en los proyectos. Conocerlos te ahorrará horas de depuración. 1. El aviso headers already sent. Es el error más buscado del desarrollo PHP y aparece cuando llamas a header() después de que el script ya haya enviado salida. Cualquier cosa cuenta: un echo, una línea de HTML, un espacio o un salto de línea fuera de las etiquetas PHP, e incluso un BOM invisible de codificación UTF-8 que algunos editores añaden al principio del archivo. Cuando el cuerpo de la respuesta ya empezó a enviarse, las cabeceras no se pueden modificar y la redirección no ocurre:
  • <?php
  • echo "Bienvenido a la tienda";
  • // en un servidor sin búfer de salida, esto ya envía HTML
  • header("Location: /inicio");
  • // Warning: Cannot modify header information - headers already sent
Para evitarlo:
  • Coloca header() y su lógica al principio del script, antes de cualquier salida: estructura el código para decidir la redirección antes de pintar HTML.
  • Omite la etiqueta de cierre ?> al final de los archivos que solo contienen PHP: así no quedan espacios ni saltos de línea posteriores que se envíen como salida.
  • Si no puedes evitar la salida previa, activa el búfer de salida con ob_start() al inicio del script; la salida queda retenida y header() puede ejecutarse después.
  • Comprueba que el archivo no se haya guardado con BOM UTF-8, porque esos tres bytes invisibles del principio también cuentan como salida.
  • Revisa que no haya espacios ni líneas en blanco antes de la etiqueta <?php de apertura.
El búfer de salida resuelve el problema cuando no puedes reordenar el código:
  • <?php
  • ob_start(); // a partir de aquí, la salida queda en el búfer
  • // ... cualquier echo o HTML queda retenido ...
  • header("Location: /inicio"); // ya no hay conflicto
  • exit;
2. Olvidar el exit después de header(). Si el script continúa, el resto del código se ejecuta: puede enviar más cabeceras, mostrar contenido o procesar datos que no debían procesarse. Acostúmbrate a escribir exit justo después de cada redirección. 3. Crear bucles de redirección. Si la URL de destino redirige otra vez a la original, el navegador entra en un ciclo y termina mostrando un error de demasiadas redirecciones. Pasa al depurar rutas protegidas: revisa que la página de login no redirija a la página que redirige al login. Una buena forma de ver la cadena completa es probar con curl desde la terminal, porque curl -I muestra solo las cabeceras de la respuesta:
  • curl -I https://www.ejemplo.com/pagina-antigua
  • HTTP/1.1 301 Moved Permanently
  • Location: https://www.ejemplo.com/pagina-nueva
4. Usar 301 para cambios que no son permanentes. Como los 301 se guardan en caché en el navegador y en los buscadores, una redirección permanente mal puesta es difícil de revertir. Reserva el 301 para movimientos definitivos y usa 302 o 303 para el resto.

En resumen

Hacer un redirect en PHP es sencillo, pero hacerlo bien exige entender HTTP. La regla que resume todo el artículo es: header("Location: URL") antes de cualquier salida, con el código de estado adecuado y seguida de exit.
  • header() con Location envía al navegador a otra URL y debe llamarse antes de cualquier salida.
  • Sin código explícito, PHP responde con un 302 temporal; usa el tercer argumento de header() o http_response_code() para enviar 301, 303 y los demás códigos.
  • El patrón PRG responde al POST con una redirección 303 para evitar duplicados al refrescar.
  • Usa redirecciones condicionales para proteger páginas y valida siempre el destino contra una lista blanca.
  • Ante el aviso headers already sent, revisa la salida previa, elimina el cierre de las etiquetas PHP y prueba con ob_start().
  • Comprueba tus redirecciones con curl -I para ver el código de estado y la URL de destino reales.
Con estos fundamentos puedes implementar redirecciones limpias, seguras y respetuosas con el SEO en cualquier proyecto PHP, desde un script pequeño hasta una aplicación completa.
Chatea por WhatsApp