php

PHP: solución headers already sent

Hoy hablaremos sobre el problema de encabezados y sesiones con PHP para solucionar el error Headers already sent.

Solución rápida

Si quieres solucionar el error Cannot modify header information – headers already sent y Warning: session_start(): Session cannot be started after headers have already been sent in en PHP, entonces verifica que:

  1. No se esté imprimiendo nada (echo, printf, HTML) antes de invocar a session_start, setcookie, header y similares en tu propio script ni en los que hayas incluido con include, require, etcétera. Las funciones que modifican los encabezados deben ser invocadas antes de enviar un byte de información al cliente
  2. Tu archivo no contenga el Byte Order Mark BOM. Utiliza este Detector de BOM para revisar el archivo PHP que ocasiona el error y todos los archivos que son incluidos.
  3. Que no exista ningún salto de línea o espacio en blanco antes de la etiqueta de apertura <?php o un bloque de HTML intercalado antes de invocar a session_start, header, setcookie, etcétera.

Solo como una solución alterna, también puedes activar y aumentar el tamaño del búfer de salida editando el archivo php.ini. En algunos casos su valor es Off pero puedes cambiarlo a output_buffering=4096 en donde 4096 es el  tamaño en bytes, mismo que puede ser aumentado según tus necesidades.

Existen más posibles soluciones y causas, te invito a leer el artículo completo para revisar la raíz de este problema.

Explicación del problema

Los encabezados HTTP no pueden ser enviados después de la salida; es obligatorio que sean enviados antes del cuerpo de respuesta. Por lo tanto, lo siguiente es incorrecto:

<?php
echo json_encode([1, 2, 3]);
header("Content-Type: application/json");

Lo correcto sería:

<?php
header("Content-Type: application/json");
echo json_encode([1, 2, 3]);

En la mayoría de casos, las sesiones y cookies van a enviar encabezados, así que las llamadas a setcookie, session_start y similares deben ser realizadas antes que cualquier otra cosa.

Por lo tanto, lo siguiente es incorrecto:

<p>Hola mundo</p>
<?php
echo json_encode([1, 2, 3]);
session_start();

Ya que todas las funciones que modifiquen o envíen encabezados deben ser invocadas antes de cualquier otra salida. Puedes hacer include o require de un archivo siempre y cuando no imprima nada ya sea con echo, printf o con bloques de código HTML.

Sorprendentemente (pero totalmente documentado), en algunos casos, los ejemplos previamente mostrados como incorrectos van a funcionar en algunos entornos, y todo es debido a la configuración del búfer de salida.

No te confíes, ya que una cosa es el modo local y otra cosa el modo producción. Es mejor conocer sobre el búfer de salida para prevenir el error headers already sent.

Sobre el búfer de salida de PHP

La directiva output_buffering importa mucho al obtener el error headers already sentSession cannot be started after headers have already been sent en PHP.

Básicamente el búfer de salida almacena todo lo que se va a enviar en la respuesta HTTP sin imprimirlo, y al terminar el script envía primero los encabezados y después todo lo almacenado en el búfer, sin importar el orden en el que cada cosa fue enviada.

Búfer con tamaño suficiente

Si tenemos un búfer lo suficientemente grande, entonces podemos enviar los encabezados en cualquier momento y no habrá ningún error.

El problema ocurre cuando la salida es más grande que el búfer o el búfer está desactivado. Veamos algunos ejemplos… ¿recuerdas el ejemplo previamente mostrado como incorrecto?

<?php
echo json_encode([1, 2, 3]); 
header("Content-Type: application/json");

Si yo lo ejecuto localmente en mi entorno (que tiene output_buffering=4096) no ocurre ningún error y el encabezado es enviado correctamente. Primero se envía el header y después el echo, aunque el orden de llamada haya sido exactamente en el orden opuesto.

Pero si yo envío una salida más grande que el valor de output_buffering con el siguiente código que imprime al menos 5000 caracteres:

<?php
echo json_encode(str_repeat("1", 5000));
header("Content-Type: application/json");

Entonces me aparece el error: Warning: Cannot modify header information – headers already sent by (output started at archivo:línea) in archivo.php on line 3

Así que dependiendo del tamaño del búfer y de la salida este error puede aparecer solo en algunos casos.

Cuando el búfer de almacenamiento está desactivado

Suponiendo que la directiva está configurada en output_buffering=Off, es decir, que el búfer de salida está desactivado (lo cual es el ajuste por defecto en varios servicios de alojamiento), entonces el ejemplo incorrecto y corto que funcionaba previamente:

<?php
echo json_encode([1, 2, 3]);
header("Content-Type: application/json");

Ya no va a funcionar. Al probarlo con esa configuración, el error será: Warning: Cannot modify header information – headers already sent by (output started at C:\xampp\htdocs\headers-already-sent\manual.php:2) in C:\xampp\htdocs\headers-already-sent\manual.php on line 3

Pasará lo mismo al iniciar una sesión:

Warning: session_start(): Session cannot be started after headers have already been sent in C:\xampp\htdocs\headers-already-sent\manual.php on line 3

Pero se va a solucionar arreglando el código para que primero se envíen todos los encabezados y después cualquier otra salida:

<?php
session_start();
echo json_encode([1, 2, 3]);

Ten cuidado con el BOM

Algunas versiones del Bloc de notas dejan el BOM o Byte Order Mark al editar un archivo. Este BOM no se imprime (digamos que es invisible) pero causa que la salida sea enviada.

Puede que te estés preguntando quién se atrevería a editar un archivo de PHP con el Bloc de notas de Windows, y aunque es poco probable, cuando uno está en la computadora local del cliente con Windows y hay que hacer una edición de último momento, el único editor instalado por defecto es el bloc de notas.

El bloc de notas puede ser solo una de tantas razones para que el archivo tenga el BOM. He desarrollado una herramienta que detecta si tu archivo tiene el BOM para que puedas removerlo.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

5 días hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.