Как исправить ошибку "Headers already sent" в PHP: причины и решения
Почему возникает ошибка "Headers already sent"?
Ошибка возникает из-за фундаментального принципа работы HTTP-протокола: заголовки должны отправляться до любого тела ответа. Когда PHP-скрипт выводит что-либо (через echo, print, пробел перед <?php или даже BOM-символ в файле), PHP немедленно отправляет HTTP-заголовки и начинает передачу тела. После этого любые попытки изменить заголовки (например, header(), setcookie(), session_start() с настройками) приводят к ошибке.
Основные причины
- Вывод до header() —
echo,print,var_dump(). - Пробелы или пустые строки перед
<?phpили после?>. - BOM-маркер в UTF-8 файлах (невидимые символы в начале файла).
- Ошибки/предупреждения PHP — они выводятся как HTML, блокируя заголовки.
Решение для вашего кода
Правильный подход — перенести всю логику редиректа до любого вывода. Вот исправленная версия:
<?php
session_start();
$user = $_POST['username'] ?? '';
$pass = $_POST['password'] ?? '';
// Проверка пользователя ДО любого вывода
if ($user === 'admin' && $pass === '12345') {
// Устанавливаем заголовок Location
header("Location: dashboard.php");
exit; // Важно: прекращаем выполнение скрипта
} else {
// Только если редирект не нужен — выводим сообщение
echo "Неверный логин или пароль";
}
?>
Обратите внимание: echo теперь находится только в ветке, где не выполняется редирект. Если нужно вывести сообщение и редиректить — используйте header() с задержкой или JavaScript:
// Вариант с meta refresh (редирект через 3 секунды)
header("Refresh: 3; URL=dashboard.php");
echo "Добро пожаловать, админ! Через 3 секунды вы будете перенаправлены.";
exit;
Как отлаживать в больших проектах
- Включите вывод всех ошибок в начале скрипта:
ini_set('display_errors', 1); error_reporting(E_ALL); ob_start(); // Буферизация вывода - Используйте буферизацию вывода — функция
ob_start()в начале скрипта перехватывает весь вывод в буфер, позволяя вызыватьheader()в любом месте. В конце буфер автоматически отправляется:<?php ob_start(); // ... весь код с header() и echo ... ob_end_flush(); // Отправляем буфер ?> - Проверяйте файлы на BOM — в редакторе (VS Code, Sublime) сохраняйте файлы в UTF-8 без BOM.
- Используйте фреймворки — Laravel, Symfony сами управляют заголовками через Response-объекты, что исключает такие ошибки.
Важные замечания
- После
header("Location: ...")всегда вызывайтеexitилиdie, чтобы скрипт не продолжал выполнение. - Функция
session_start()также устанавливает заголовки (Cookie), поэтому её тоже нужно вызывать до любого вывода. - Для отладки используйте
headers_list()— она покажет все отправленные заголовки.
Альтернативный подход: использование JavaScript для редиректа
Если вам нужно вывести сообщение пользователю перед редиректом, можно отказаться от PHP-заголовков в пользу JavaScript. Это особенно полезно, когда логика вывода сложная или вы не можете контролировать буферизацию:
<?php
$user = $_POST['username'] ?? '';
$pass = $_POST['password'] ?? '';
if ($user === 'admin' && $pass === '12345') {
echo "Добро пожаловать, админ!";
echo "<script>setTimeout(function() { window.location.href = 'dashboard.php'; }, 3000);</script>";
exit;
} else {
echo "Неверный логин или пароль";
}
?>
Использование ob_start() для глобального решения
В legacy-проектах, где много файлов с потенциальным выводом, можно включить буферизацию на уровне конфигурации:
// В начале каждого скрипта или в общем файле конфигурации
ob_start();
// В конце скрипта
register_shutdown_function(function() {
ob_end_flush();
});
Или в php.ini установить output_buffering = 4096 — это включит буферизацию для всех скриптов.
Проверка на вывод перед header()
Для отладки используйте функцию ob_get_level() — если она больше 0, буферизация активна. А ob_get_contents() покажет, что уже было выведено:
if (ob_get_level() > 0) {
$output = ob_get_contents();
error_log("Вывод до header(): " . $output);
ob_end_clean(); // Очищаем буфер
}
header("Location: dashboard.php");
Этот подход помогает быстро локализовать источник нежелательного вывода в больших проектах.
Ответов пока нет.
Войдите чтобы оставить ответ