Si estás viendo el mensaje Cannot modify header information – headers already sent en tu WordPress, respira. No se ha roto nada grave. Lo que ha pasado es que tu PHP ha intentado enviar una cabecera HTTP (una redirección, una cookie, un tipo de contenido) cuando ya había mandado texto al navegador. Y una vez que el navegador recibe el primer byte de HTML, las cabeceras ya viajaron. No hay vuelta atrás.
Este aviso aparece sobre todo en plugins mal programados, en themes viejos y en archivos functions.php donde alguien dejó un espacio antes de abrir <?php. Es un error clásico de PHP, no un fallo del núcleo de WordPress, y en 2026 sigue siendo igual de frecuente. La buena noticia: se soluciona en minutos si sabes dónde mirar.
Qué significa exactamente “headers already sent” en WordPress
Cuando PHP envía una página, primero manda las cabeceras (status code, cookies, redirects) y luego el cuerpo HTML. El problema es que si cualquier cosa imprime un carácter antes de que tu código llame a header(), setcookie() o session_start(), PHP ya ha cerrado la fase de cabeceras. Entonces salta el warning.
La frase clave que suele acompañar al error es “output started at /ruta/archivo.php:12”. Ese archivo y esa línea son tu pista principal. No es decorativo: te está diciendo exactamente dónde se envió el primer byte. Ignorarlo y buscar a ciegas es perder el tiempo.
En WordPress, los culpables habituales son el archivo wp-config.php, el functions.php del tema activo, un archivo de plugin con un espacio antes de la etiqueta de apertura, o un BOM UTF-8 invisible al principio de un archivo. Ese BOM es traicionero porque no lo ves en el editor, pero PHP sí lo cuenta como salida.
La solución directa: colocar header() antes de cualquier salida
Si estás programando una redirección manual, la regla es simple: header() tiene que ser la primera instrucción que produce salida. Nada de espacios, nada de HTML previo, nada de ecos accidentales.
<?php
header(«Location: https://tudominio.com/otra-pagina/»);
exit();
?>
El exit() no es opcional. Si no lo pones, PHP sigue ejecutando el resto del script y puede generar contenido que rompa la redirección. Muchos tutoriales lo omiten y luego vienen los sustos.
En WordPress, además, tienes funciones específicas que evitan líos: wp_redirect() y wp_safe_redirect(). Úsalas dentro de hooks como template_redirect o admin_init, nunca antes de que WordPress esté cargado. Si llamas a wp_redirect() en la primera línea de un plugin sin hook, el error está garantizado.
Soluciones cuando el error aparece en un plugin o tema de WordPress
Aquí va lo que yo haría en orden, sin dar palos de ciego:
- Revisa el archivo que indica el error. Abre la ruta exacta y mira si hay un espacio, un salto de línea o texto antes de <?php.
- Elimina el BOM UTF-8. En VS Code o Notepad++ guarda el archivo como “UTF-8 sin BOM”. Este detalle arregla más casos de los que la gente cree.
- Desactiva plugins uno a uno. Si el error desaparece, ya sabes quién es el culpable. Luego actualiza o reemplaza ese plugin.
- Cambia a un tema por defecto (Twenty Twenty-Four o similar) para descartar el theme.
- Activa WP_DEBUG en wp-config.php para ver la ruta exacta: define(‘WP_DEBUG’, true); y define(‘WP_DEBUG_LOG’, true);.
Si el aviso solo sale en el panel de administración, suele ser un plugin que imprime algo en admin_init antes de una redirección. Si sale en el frontend, casi siempre es el tema o un plugin de caché mal configurado.
El truco del output buffering: cuándo sí y cuándo no
Mucha gente recurre a ob_start() al principio del archivo para retener la salida y poder enviar cabeceras después. Funciona, pero no es magia. Si abres un buffer y no lo cierras bien, puedes romper la salida de WordPress y cargarte la paginación o el RSS.
El output buffering es un parche, no un arreglo. Sirve cuando no controlas el código que imprime (por ejemplo, un plugin de terceros) y necesitas salir del paso. Pero si puedes corregir el origen, corrígelo. Un buffer abierto durante toda la ejecución consume memoria y en sitios con mucho tráfico puede pasar factura.
En WordPress existe una alternativa más limpia: usar el hook init o template_redirect para tus redirecciones. Así te aseguras de que no ha salido nada antes y no necesitas buffer.
Alternativas con JavaScript y meta refresh (y sus límites)
Cuando no puedes tocar el PHP porque el hosting está bloqueado o el plugin es intocable, puedes redirigir desde el lado del cliente. JavaScript y meta refresh son las dos opciones clásicas.
<script>window.location.href = «https://tudominio.com/otra-pagina/»;</script>
<meta http-equiv=»refresh» content=»0;URL=’https://tudominio.com/otra-pagina/'»>
Funcionan, sí, pero ojo: si el usuario tiene JavaScript desactivado o un bloqueador agresivo, la redirección no ocurre. Además, para SEO no es lo mismo un redirect 301 que un salto por JavaScript. Google lo entiende, pero no lo trata igual. Úsalo como último recurso, no como estrategia.
El meta refresh además puede ser penalizado si abusas de él. Es aceptable para casos puntuales, no para migraciones de dominio ni cambios de URL masivos. Para eso, siempre 301 desde el servidor.
Errores que se repiten y que deberías evitar en 2026
He visto demasiadas veces el mismo patrón: alguien añade un snippet en functions.php, deja un espacio después de ?> y a los dos días el sitio escupe el warning. Regla de oro: nunca cierres el archivo PHP con ?>. No es obligatorio y solo trae problemas.
Otro clásico es llamar a session_start() en un plugin después de que WordPress ya ha enviado cabeceras. Si necesitas sesiones, hazlo antes de que el núcleo arranque o usa transients de WordPress, que están pensados justo para eso.
Y un detalle que muchos pasan por alto: errores de URL en la redirección también pueden disparar avisos relacionados. Si la URL destino tiene caracteres raros o está mal formada, PHP puede quejarse. Revisa siempre que la ruta sea válida y absoluta.
El error Cannot modify header information no es un misterio: es PHP diciéndote que llegas tarde. Encuentra la salida temprana, elimina espacios y BOM, usa los hooks correctos de WordPress y, si no te queda otra, tira de output buffering o de JavaScript con conocimiento de causa.