Как исправить ошибку "Headers already sent" в PHP: пошаговое руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Знакомьтесь: "Headers already sent" — ваш новый враг (и друг)

Если вы хоть раз писали на PHP, вы наверняка встречались с этой ошибкой. Она появляется внезапно, часто в самый неподходящий момент — когда вы уже почти закончили скрипт. Сообщение пугающее, красное, и кажется, что весь код сломался. Но на самом деле это одна из самых полезных ошибок в PHP. Она учит вас правильно работать с HTTP-протоколом.

В этом туториале мы разберём, почему возникает ошибка Headers already sent, как её диагностировать и, главное, как исправить раз и навсегда. Мы не просто скопируем ответ со Stack Overflow — мы поймём суть. Поехали!

Что такое HTTP-заголовки и почему PHP на них ругается?

Прежде чем лечить, нужно понять анатомию. Когда PHP отправляет ответ браузеру, он делает это в два этапа:

  • Сначала — заголовки (headers). Это служебная информация: тип контента (Content-Type), кодировка, куки, статус ответа (200 OK, 404 Not Found).
  • Потом — тело ответа (body). Это сам HTML, текст, JSON — то, что видит пользователь.

Проблема в том, что PHP не может отправить заголовки после того, как начал отправлять тело. Если вы попытаетесь вызвать header() после того, как уже вывели хоть один символ (даже пробел), PHP выдаст ошибку:

Warning: Cannot modify header information - headers already sent by (output started at /path/to/file.php:12)

Вот и вся магия. Ошибка не фатальная — она просто предупреждает, что ваш header() не сработал. Но последствия могут быть серьёзными: редирект не выполнится, кука не установится, JSON-ответ сломается.

Топ-5 причин, почему это происходит

Давайте разберём самые частые сценарии. Узнаёте себя?

1. Пробел или пустая строка перед <?php

Это классика. Вы открываете файл, а перед открывающим тегом <?php стоит пробел, пустая строка или символ BOM (Byte Order Mark). PHP воспринимает этот символ как часть вывода — и понеслось.

// ❌ Ошибка: пробел перед <?php
 <?php
header('Location: /new-page.php');
?>
// ✅ Правильно: никаких символов до <?php
<?php
header('Location: /new-page.php');
?>

2. Вывод через echo или print до header()

Логичная, но частая ошибка новичков: сначала что-то вывести, а потом попытаться перенаправить пользователя.

<?php
echo "Привет, мир!";
header('Location: /thanks.php'); // ❌ Ошибка: вывод уже был
?>

3. HTML-код до PHP-блока

В файле может быть смешанный код: сначала HTML-разметка, потом PHP-логика с header().

<!DOCTYPE html>
<html>
<head>
    <title>Мой сайт</title>
</head>
<body>
<?php
header('Location: /login.php'); // ❌ Ошибка: HTML уже отправлен
?>
</body>
</html>

4. Ошибки PHP и функции вроде var_dump()

Скрипт может выводить предупреждения или отладочную информацию до вызова header(). Например, если у вас включён display_errors и PHP выводит warning.

<?php
var_dump($some_variable); // Вывод данных
header('Content-Type: application/json'); // ❌ Ошибка
?>

5. Невидимые символы в подключаемых файлах (include/require)

Вы подключаете файл с помощью include или require, а в конце этого файла есть лишняя пустая строка или пробел после закрывающего ?>.

// config.php
<?php
$db_host = 'localhost';
?>
 // ← Вот этот пробел после ?> всё ломает!

Как найти источник проблемы: пошаговая диагностика

Самое главное — ошибка всегда указывает, где именно начался вывод. Читайте сообщение внимательно: output started at /path/to/file.php:12 — это строка 12 вашего файла. Откройте её и посмотрите, что там.

Шаг 1. Проверьте файл, указанный в ошибке

Откройте конкретный файл и строку. Скорее всего, вы увидите echo, пробел, HTML или пустую строку.

Шаг 2. Временно включите все ошибки

Добавьте в начало скрипта:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
?>

Это покажет все предупреждения, которые могут быть причиной неявного вывода.

Шаг 3. Используйте функцию headers_sent()

Перед вызовом header() проверьте, не были ли уже отправлены заголовки:

<?php
if (!headers_sent()) {
    header('Location: /new-page.php');
    exit;
} else {
    echo "Заголовки уже отправлены. Редирект невозможен.";
}
?>

Три способа исправить ошибку (от простого к продвинутому)

Теперь переходим к самому интересному — как это починить. У вас есть три пути.

Способ 1. Перенесите header() до любого вывода (рекомендуемый)

Это правильное архитектурное решение. Функции, изменяющие заголовки, должны вызываться в самом начале скрипта, до любого HTML или текста.

<?php
// Всё, что меняет заголовки — в самом начале
header('Content-Type: text/html; charset=utf-8');
session_start();

// Потом — остальной код
echo "Добро пожаловать!";
?>

Если вам нужно сделать редирект после какой-то логики — вынесите её до вывода:

<?php
// Логика проверки
$user_logged_in = false;

if (!$user_logged_in) {
    header('Location: /login.php');
    exit; // Всегда вызывайте exit после header('Location:')
}

// HTML только после проверок
?>
<!DOCTYPE html>
<html>...</html>

Способ 2. Используйте буферизацию вывода (ob_start)

Если вы не можете контролировать весь код (например, работаете с легаси-проектом или чужой библиотекой), включите буферизацию. Весь вывод будет накапливаться в буфере и отправляться только после завершения скрипта.

<                

Блоги

Рекомендаций по книгам