Ошибка 'Headers already sent' в PHP: Полное руководство по решению проблемы

Ошибка 'Headers already sent' в PHP: Полное руководство по решению проблемы

Вы работаете с PHP-скриптом, и внезапно сталкиваетесь с загадочной ошибкой "Cannot modify header information - headers already sent". Эта ошибка способна вывести из себя даже опытного разработчика, прерывая выполнение скрипта и ломая функциональность сайта. Но не стоит паниковать — эта ошибка имеет логичное объяснение и несколько эффективных способов решения, которые мы подробно разберем в этой статье.

Что такое HTTP-заголовки и почему они так важны?

Прежде чем понять ошибку, нужно разобраться в механизме работы HTTP-заголовков. Когда браузер запрашивает страницу, сервер отправляет ответ, состоящий из двух частей: заголовков (headers) и тела (body). Заголовки содержат служебную информацию: тип контента, кодировку, куки, редиректы и статус ответа. Тело — это непосредственно HTML-код, который видит пользователь.

Ключевое правило: заголовки должны отправляться ДО любого вывода в браузер. Как только PHP отправил хотя бы один символ (пробел, перевод строки, HTML-тег), отправка дополнительных заголовков становится невозможной.

Основные причины ошибки "Headers already sent"

Ошибка возникает, когда вы пытаетесь изменить заголовки после того, как PHP уже начал вывод данных. Вот самые распространенные причины:

1. Пробелы и пустые строки перед тегом

Самая частая причина — невидимые символы в начале или конце файлов:

  • Пробелы перед открывающим тегом
  • Пустые строки после закрывающего тега ?> в подключаемых файлах
  • Символы BOM (Byte Order Mark) в файлах с кодировкой UTF-8

2. Вывод данных до функций header(), setcookie(), session_start()

Любой вывод, даже случайный:

  1. echo, print, printf до вызова header()
  2. HTML-код вне PHP-тегов
  3. Ошибки PHP, которые выводятся на экран
  4. Вывод из подключаемых файлов (include, require)

3. Проблемы с буферизацией вывода

По умолчанию PHP отправляет данные браузеру сразу. Без буферизации любой вывод сразу становится частью ответа.

Практические решения: пошаговый разбор

Решение 1: Проверка файлов на лишние символы

Откройте все связанные файлы в текстовом редакторе с отображением невидимых символов. Убедитесь, что:

  • Перед
  • После ?> (если он есть) тоже нет ничего
  • Файлы сохранены без BOM (в большинстве редакторов есть опция "Save without BOM")

Совет: вообще избегайте закрывающего тега ?> в файлах, содержащих только PHP-код. Это предотвратит случайное добавление символов после него.

Решение 2: Использование буферизации вывода

Функция ob_start() включает буферизацию — весь вывод накапливается в буфере и отправляется только в конце скрипта:

// В начале скрипта
ob_start();
// ... ваш код с header() и setcookie()
// В конце скрипта
ob_end_flush();

Решение 3: Реорганизация кода

Перенесите все операции с заголовками в самое начало скрипта, до любого вывода:

  1. session_start() — первой строкой
  2. Все header() и setcookie() — сразу после
  3. Проверки и обработка данных
  4. Вывод HTML только после всех заголовков

Решение 4: Включение логов ошибок

Часто ошибка содержит указание на файл и строку, где начался вывод. Включите подробное логирование:

ini_set('display_errors', 1);
error_reporting(E_ALL);

Профилактика: как избежать ошибки в будущем

  • Используйте единую точку входа (front controller)
  • Применяйте шаблонизаторы, которые отделяют логику от представления
  • Настройте IDE на отображение невидимых символов
  • Пишите тесты, которые проверяют корректность отправки заголовков
  • Используйте автоматический анализ кода (PHPStan, Psalm)

FAQ: Часто задаваемые вопросы

Почему ошибка появляется только на хостинге, а локально всё работает?

На локальном сервере может быть включена буферизация вывода или другой уровень отображения ошибок. Проверьте настройки php.ini на хостинге.

Как найти, где именно начался вывод?

Включите вывод всех ошибок (error_reporting(E_ALL)) — часто в сообщении об ошибке указан файл и номер строки, где произошел первый вывод.

Можно ли использовать @ перед header() чтобы подавить ошибку?

Нет! Оператор @ только скроет ошибку, но не решит проблему. Заголовки всё равно не будут отправлены корректно.

Почему session_start() тоже вызывает эту ошибку?

Функция session_start() пытается отправить cookie с идентификатором сессии, что требует отправки заголовков. Её нужно вызывать до любого вывода.

Как удалить BOM из файлов?

Используйте редакторы типа Notepad++ (Encoding → Encode in UTF-8 without BOM) или специальные утилиты. В командной строке: sed -i '1s/^\xEF\xBB\xBF//' file.php