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.
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;
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. |
- header("Location: https://www.ejemplo.com/nueva-pagina", true, 301);
- exit;
- http_response_code(301);
- header("Location: https://www.ejemplo.com/nueva-pagina");
- exit;
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;
- }
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
- <?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;
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;
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
- 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.
- <?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;
- curl -I https://www.ejemplo.com/pagina-antigua
- HTTP/1.1 301 Moved Permanently
- Location: https://www.ejemplo.com/pagina-nueva
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.