„Cannot modify header information – headers already sent” – jak znaleźć przyczynę
Logowanie nie działa, przekierowanie nie działa, a w logu jest „headers already sent”? Komunikat sam mówi, gdzie siedzi błąd – trzeba tylko wiedzieć, gdzie patrzeć.
W skrócie: komunikat oznacza, że PHP wysłał już coś do przeglądarki, zanim kod próbował ustawić przekierowanie, ciasteczko albo sesję. Kolejność musi być taka: najpierw nagłówki, potem treść. Przyczyna jest w samym komunikacie po słowach „output started at”: plik i linia, w której nastąpiło pierwsze wyjście. Najczęściej to spacja albo pusta linia przed <?php lub po ?>, niewidoczny BOM na początku pliku albo wyświetlone ostrzeżenie.
Warning: Cannot modify header information - headers already sent by
(output started at /var/www/html/config.php:24) in /var/www/html/login.php on line 15
Skutki: po zalogowaniu wraca formularz logowania, przekierowania nie działają, pobieranie pliku się przerywa, a w WordPressie po zapisaniu strona przestaje się ładować.
Jak czytać komunikat
Komunikat podaje dwa miejsca:
- „output started at config.php:24” – tu jest problem. W tym miejscu PHP wysłał już tekst do przeglądarki.
- „in login.php on line 15” – tu problem wyszedł na jaw. Ta linia chciała ustawić nagłówek, np. przez
header()albosetcookie(). Przysession_start()komunikat brzmi nieco inaczej („Session cannot be started after headers have already been sent”), ale przyczyna jest ta sama.
Prawie wszyscy patrzą na drugie miejsce. Naprawić trzeba pierwsze.
Najczęstsze przyczyny
1. Spacje albo puste linie poza PHP
Wszystko, co jest przed <?php albo po ?>, PHP wysyła jako tekst, także spację czy pustą linię. Pomija tylko pojedynczy znak nowej linii zaraz po ?>. Szczególnie zdradliwa jest pusta linia na końcu plików zawierających tylko PHP, np. plików konfiguracyjnych albo functions.php motywu WordPress.
Rozwiązanie: w plikach z samym PHP całkowicie pominąć zamykające ?> na końcu. Jest to dozwolone i zalecane. Przed <?php na początku nie może być nic.
2. BOM na początku pliku
Niektóre edytory zapisują pliki jako „UTF-8 z BOM”. BOM to trzy niewidoczne bajty na samym początku. W edytorze nic nie widać, ale PHP je wysyła. Jeśli komunikat wskazuje linię 1, to najbardziej prawdopodobna przyczyna.
Rozwiązanie: zapisać plik jako „UTF-8 bez BOM”. W Notepad++ to „UTF-8” zamiast „UTF-8-BOM”, w VS Code przez kodowanie w prawym dolnym rogu.
3. Wyjście w kodzie
Zapomniane echo, print_r() albo var_dump() z testów albo HTML wysyłany przed logiką.
4. Ostrzeżenia i komunikaty
Gdy display_errors jest włączone, PHP wysyła ostrzeżenia jako tekst, np. „Undefined array key”. To wyjście też się liczy i blokuje potem każdy nagłówek. Dlatego po aktualizacji PHP nagle pojawiają się komunikaty „headers already sent”, choć w kodzie nic nie zmieniano.
Rozwiązanie: usunąć właściwe ostrzeżenie i na produkcji wyłączyć display_errors.
W WordPressie
Jeśli „output started at” wskazuje wp-config.php albo functions.php motywu, prawie zawsze jest tam spacja albo pusta linia na początku lub końcu, często po zmianie w edytorze motywu. Jeśli komunikat wskazuje wtyczkę, trzeba ją zaktualizować albo wyłączyć. Gdy panel nie działa, pomaga zmiana nazwy folderu wtyczki przez FTP, jak w artykule o błędzie krytycznym WordPressa.
Dlaczego output buffering to nie rozwiązanie
Często poleca się dodać ob_start() na początku albo włączyć output_buffering w konfiguracji PHP. PHP najpierw zbiera wtedy wyjście i wysyła je później, a komunikat znika. Przyczyna jednak zostaje: puste linie, testowe wyjścia albo ostrzeżenia nadal trafiają na stronę, np. w środek odpowiedzi JSON albo przed pobierany plik PDF. Jako tymczasowe obejście się nadaje, jako naprawa nie.
Profesjonalna pomoc
W rozbudowanych aplikacjach z wieloma dołączanymi plikami miejsce nie zawsze jest oczywiste, zwłaszcza gdy wyjście powodują ostrzeżenia. Znajduję przyczynę i usuwam ją na stałe. Więcej w usłudze naprawa błędów PHP.