Вы работаете над PHP-проектом, всё идёт хорошо, и вдруг — белая страница или странное сообщение об ошибке «Cannot modify header information - headers already sent». Знакомо? Эта ошибка — одна из самых частых и раздражающих проблем в PHP-разработке, которая может заставить потратить часы на поиск лишнего пробела или невидимого символа. Но не отчаивайтесь! В этой статье мы глубоко разберём природу этой ошибки, все её причины и, главное, — эффективные способы решения и профилактики.
Что такое HTTP-заголовки и почему они так важны?
Прежде чем бороться с ошибкой, нужно понять её суть. Когда браузер запрашивает страницу, сервер отправляет ему не только HTML-код, но и служебную информацию — HTTP-заголовки. Они идут самыми первыми, до любого вывода на экран. В заголовках передаётся тип контента, кодировка, куки, редиректы (например, через header('Location: ...')) и многое другое.
Ключевой момент: PHP может отправить заголовки только один раз и только до того, как в поток вывода попадёт хоть один байт «тела» ответа (обычный текст, HTML, пробел, перевод строки).
Типичные причины ошибки «Headers already sent»
Ошибка возникает, когда вы пытаетесь изменить заголовки (например, с помощью функций header(), setcookie(), session_start()), но PHP уже начал выводить контент. Вот главные виновники:
1. Случайный вывод до тега
Самая частая причина — пробелы, переводы строк или любой текст в файле перед открывающим тегом или после закрывающего ?>.
2. Вывод в подключаемых файлах (include/require)
Если подключаемый файл содержит пробелы после ?>, они «просачиваются» в основной скрипт.
3. UTF-8 с BOM (Byte Order Mark)
Невидимые служебные символы BOM в начале файла, которые добавляются некоторыми редакторами при сохранении в UTF-8. Они и есть тот самый «лишний байт».
4. Вывод данных до вызова header()
Любой echo, print, HTML-код вне PHP-блоков или даже ошибки PHP/предупреждения, выводящиеся на экран, сделают отправку заголовков невозможной.
Пошаговое руководство по исправлению
- Включите детальный вывод ошибок. Добавьте в начало скрипта:
- ini_set('display_errors', 1);
- ini_set('display_startup_errors', 1);
- error_reporting(E_ALL);
- Проверьте файлы на наличие BOM. Используйте продвинутые редакторы (VS Code, PhpStorm, Notepad++) и сохраняйте файлы в UTF-8 без BOM.
- Уберите всё перед . Идеально — вообще не закрывать тег ?> в файлах, содержащих только PHP-код. Это предотвратит случайные пробелы.
- Используйте буферизацию вывода (ob_start()). Поместите ob_start() в самом начале скрипта. Эта функция временно накапливает весь вывод, позволяя отправлять заголовки в любой момент. Не забывайте про ob_end_flush() в конце.
- Проверьте логи веб-сервера и PHP. Иногда причиной могут быть ошибки, записанные в лог, но не отображаемые на экране.
Профилактика — лучшее лечение: Соблюдайте чистую архитектуру. Все операции с заголовками (редиректы, установка кук, сессий) выполняйте в самом начале скрипта, до любого HTML-вывода. Используйте шаблонизаторы и разделение логики (Controller) и отображения (View).
FAQ: Часто задаваемые вопросы
Ошибка появляется, даже когда в коде нет echo или HTML. В чём дело?
Скорее всего, виноват файл с UTF-8 BOM или пробелы в подключаемом файле (например, config.php). Проверьте все include/require.
Как навсегда избавиться от этой проблемы в своих проектах?
Придерживайтесь стандартов кодирования (например, PSR-1/2). Не используйте закрывающий тег ?> в файлах только с PHP-кодом. Включайте буферизацию на уровне приложения (например, в index.php).
Можно ли игнорировать эту ошибку?
Нет! Если вы используете header() для редиректа или setcookie() — они не сработают. Сессии могут начаться некорректно. Это критическая ошибка для функциональности.
Помогает ли @ перед header()?
Оператор @ подавит сообщение об ошибке, но не решит проблему. Заголовки всё равно не отправятся, а логика сломается. Это худшее решение.