← Terug naar kennisbank HOSTING

"Cannot modify header information — headers already sent" — uitgelegd en opgelost

Een van de oudste en meest verwarrende PHP-fouten — vaak na een editor-wissel, een config-edit of een server-migratie: "Warning: Cannot modify header information - headers already sent by (output started at /path/file.php:1)". Het foutbericht klinkt cryptisch maar bevat letterlijk het antwoord. Deze gids legt uit wat 't betekent, hoe je in 30 seconden de oorzaak vindt, en wat je doet om 't structureel weg te houden.

De fout ziet er typisch zo uit:

Warning: Cannot modify header information - headers already sent
by (output started at /home/site/wp-config.php:1) in
/home/site/wp-includes/pluggable.php on line 1421

Of bij een loginscherm met sessies:

Warning: session_start(): Cannot send session cookie -
headers already sent by (output started at /home/site/index.php:3)

Of bij een setcookie-call:

Warning: Cannot modify header information - headers already sent

Bij sommige sites zie je naast de warning ook witte pagina's, mislukte logins, niet-werkende redirects of session-cookies die niet worden gezet. Dat is hetzelfde onderliggende probleem.

Wat betekent het?

HTTP heeft een strikte volgorde: eerst headers, dan body. Headers zijn de metadata (cookies, redirect-instructies, content-type, status-code), de body is de eigenlijke pagina-inhoud die de bezoeker ziet.

Zodra ook maar één byte body de browser uit gaat, sluit PHP de headers af. Probeert je code daarna nog een header te zetten — via header(), session_start(), setcookie(), of session_regenerate_id() — dan is dat te laat. PHP geeft een warning en de header wordt genegeerd.

De vraag is dus altijd dezelfde: welke output ontsnapte er voortijdig? En het foutbericht zegt het je woordelijk:

output started at /home/site/wp-config.php:1

Vertaling: in wp-config.php, op regel 1, begon er output te lopen die niet had mogen lopen. Dáár ga je kijken.

De zes meest voorkomende oorzaken

1. BOM (Byte Order Mark) in een UTF-8 bestand

Verreweg de meest voorkomende oorzaak. Een BOM is een onzichtbaar 3-byte teken (EF BB BF in hex) dat sommige editors automatisch vooraan UTF-8 bestanden plaatsen. Voor PHP zijn die drie bytes output — de header is daarmee al verstuurd voordat ook maar één regel code is uitgevoerd.

Herken je aan: output started at file.php:1 in de fout. Lijn 1 met geen zichtbare reden = altijd BOM-verdacht.

Veelvoorkomende boosdoeners: wp-config.php dat in Notepad of een andere "rich" editor is geopend en opgeslagen, custom plugin-bestanden van een ontwikkelaar die in zo'n editor werkte, theme-bestanden gedownload van marketplaces.

Detectie via SSH:

head -c 3 wp-config.php | xxd
# Geeft "EF BB BF"  → BOM aanwezig (probleem)
# Geeft alleen ASCII → geen BOM

Of via PHP zelf:

$content = file_get_contents('wp-config.php');
echo bin2hex(substr($content, 0, 3));
// EFBBBF = BOM

Fix — éénmalig: open het bestand in een editor die "Save without BOM" ondersteunt (VS Code, Notepad++, Sublime Text) en sla 't opnieuw op:

  • VS Code: rechtsonder klik op "UTF-8 with BOM" → kies "UTF-8" (zonder BOM) → save
  • Notepad++: menu Encoding → "Encode in UTF-8" (NIET "with BOM") → save
  • Sublime Text: File → "Save with Encoding" → "UTF-8"

Fix — meerdere bestanden tegelijk via SSH:

# Verwijder BOM uit alle PHP-bestanden in een directory
find . -name "*.php" -exec sed -i '1s/^\xEF\xBB\xBF//' {} \;

Maak eerst een backup. Dit commando schrijft direct in alle gevonden bestanden.

2. Whitespace vóór <?php

Een spatie of lege regel boven de openingstag wordt door PHP als output gezien. Vaak ontstaan na het pasten van code of het bewerken in een editor die automatisch een lege eerste regel toevoegt.

(lege regel of spatie)
<?php
// rest van de code

Fix: open het bestand in een editor met "show whitespace" aan, navigeer naar regel 1, zorg dat <?php letterlijk de eerste tekens van het bestand zijn — geen newline, geen spatie ervoor.

3. Whitespace of nieuwe regel ná ?>

De omgekeerde variant. Bestand eindigt met ?> gevolgd door een newline, twee newlines, of een spatie. Komt veel voor bij included bestanden — als je hoofdscript een ander PHP-bestand inlaadt en dát bestand heeft trailing whitespace, lekt die door als output van het hoofdscript.

<?php
// rest van de code
?>
(spatie of newline hier — boosdoener)

Fix met preventie: laat de afsluitende ?> weg in bestanden die alleen PHP bevatten. Zonder afsluitende tag kan PHP zelf nooit per ongeluk trailing whitespace meesturen. WordPress core, modern frameworks en de PSR-2-stijlgids volgen dit. Dus simpel:

<?php
// rest van de code
// (geen ?> aan het eind)

4. Echo, print of var_dump vóór een header()-call

Klassieker bij debug-sessies. Je voegt tijdelijk echo "test" of var_dump($variabele) toe om te kijken wat er aankomt, en daarna pas roept je code header('Location: ...') aan voor een redirect. PHP heeft inmiddels output gestuurd → header faalt.

<?php
echo "Even debuggen"; // OUTPUT VERSTUURD
header('Location: /dashboard.php'); // FAALT

Fix: verwijder de debug-output, of pas output buffering toe (zie verderop).

5. HTML-output vóór PHP-code die headers wil zetten

Bij sommige (vaak oudere) sites:

<!DOCTYPE html>
<html>
<?php
// User check
if (!ingelogd()) {
    header('Location: /login.php'); // TE LAAT
}
?>

De <!DOCTYPE html>-regel is al naar de browser gestuurd voordat PHP überhaupt aan het denken slaat.

Fix: zet alle redirect- en authenticatie-logica bovenaan het bestand, vóór ook maar één byte HTML. Ander volgorde:

<?php
if (!ingelogd()) {
    header('Location: /login.php');
    exit;
}
?>
<!DOCTYPE html>
<html>
...

Belangrijk: voeg altijd exit; of die(); toe direct na een redirect-header, anders blijft de rest van het script alsnog draaien.

6. Error of warning verschijnt vóór header()

Als display_errors aanstaat en er gebeurt eerder in het script een notice of warning ("Undefined index", "Deprecated", etc.), dan wordt die foutmelding ook als output beschouwd. Daarna faalt elke header()-call.

Fix: zet display_errors uit op productie en log naar bestand:

ini_set('display_errors', '0');
ini_set('log_errors', '1');

De warnings blijven dan in je error_log staan zonder de header-flow te breken. Daarna pak je de onderliggende warnings aan.

Diagnose-stappenplan — vind de oorzaak in 5 minuten

  1. Lees de fouttekst letterlijk. output started at /pad/file.php:N wijst exact aan welk bestand en regel
  2. Open dat bestand. Eerste check: zit er een BOM voor regel 1? (Gebruik xxd, hexdump, of een editor die BOM zichtbaar maakt)
  3. Tweede check: is regel 1 echt <?php zonder iets ervoor? (Geen spatie, geen lege regel)
  4. Derde check: kijk waar in dit bestand iets ge-echoot, geprint of ge-output zou kunnen worden vóór de regel die de header probeert te zetten
  5. Bij een include of require: check ook het bestand dat ingeladen wordt — vaak zit de BOM of trailing whitespace in een included file
  6. Logs checken: kijk in je server error_log of WordPress' debug.log voor de hele context. De header-warning zelf is vaak een symptoom van een eerdere notice/warning die de output veroorzaakte

Output buffering — de noodfix die problemen kan maskeren

Een veel-gegeven "snelle oplossing" is output buffering activeren bovenin je script:

<?php
ob_start();
// rest van de code

Of globaal in php.ini / .user.ini:

output_buffering = On

Wat 't doet: PHP houdt alle output in een buffer in plaats van direct te versturen. Headers kunnen dan gezet worden zolang de buffer nog niet is leeggemaakt.

Waarom 't géén echte oplossing is: het maskeert de root cause. Als de oorzaak BOM in een bestand is, zit die er nog steeds. Bij een PHP-versie-upgrade die buffering anders afhandelt, of bij een hosting-migratie, popt het probleem opnieuw op. Beter fix de oorzaak, gebruik buffering alleen als tijdelijke workaround.

Specifiek voor WordPress

De foutmelding die op WordPress regelmatig opduikt:

Warning: Cannot modify header information - headers already sent
by (output started at /home/site/wp-config.php:1) in
/home/site/wp-includes/pluggable.php on line 1421

Bijna altijd: BOM of trailing whitespace in wp-config.php. Veroorzaakt onder meer:

  • Inloggen op wp-admin werkt niet (sessions)
  • "Headers already sent" boven elke pagina
  • Soms onverklaarbare "Error establishing a database connection"-meldingen

Fix: open wp-config.php in VS Code, controleer encoding (rechtsonder), kies "UTF-8 zonder BOM", save.

Andere veelvoorkomende WordPress-locaties:

  • wp-content/themes/jouw-theme/functions.php — vaak vóór <?php een spatie
  • wp-content/plugins/.../*.php — custom plugin met whitespace
  • .htaccess bevat bytes die PHP-init verstoren (zeldzaam)

Tip: zet WP_DEBUG_LOG aan (zie WordPress critical error oplossen) zodat je de volledige context van de warning ziet, niet alleen de eerste regel.

Specifiek voor andere CMS-en

  • Joomla: vaak in configuration.php. Open in editor zonder BOM en sla opnieuw op
  • Drupal: sites/default/settings.php of een custom module
  • OpenCart: config.php of admin/config.php
  • CMS Made Simple: config.php of een aangepast UDT (User Defined Tag)
  • Custom PHP: meestal in een included config-bestand of helper-script — niet in het hoofdbestand

Preventie

  • Editor-instelling: zet de default-encoding op "UTF-8 zonder BOM" in je editor. Eénmalig instellen, levenslang voorkomen
  • Laat ?> weg in bestanden die alleen PHP bevatten. Geen kans op trailing whitespace
  • Authenticatie en redirects bovenaan: alle header()-calls in de eerste paar regels van het script, vóór elke HTML
  • Bewerk wp-config.php / configuration.php nooit in Notepad of TextEdit — die voegen vaak BOM toe of regelafsluiters die je server niet verwacht. Gebruik VS Code, Notepad++, Sublime Text of een vergelijkbare code-editor
  • Bij downloaden van zip-bestanden die je gaat uploaden: pak ze uit, open de PHP-bestanden in je editor en check op BOM voor je ze deployt
  • display_errors = Off op productie. Voorkomt dat een notice je hele header-flow breekt
Tip: de fout zegt letterlijk waar de output begon — pad én regelnummer. Als er :1 staat is 't bijna zeker BOM of leading whitespace. Bij een hoger regelnummer kijk je gewoon naar dáár wat er ge-echoot wordt vóór de header. Dit is een fout die zelden mysterieus blijft als je het bericht aandachtig leest.

Wanneer schakel je hulp in?

  • De fouttekst wijst naar een bestand maar daar zit geen zichtbare BOM of whitespace, en je weet niet waar de output anders vandaan komt
  • Site met tientallen included files en je weet niet welk bestand de boosdoener is
  • Combineert met andere errors (sessions die niet werken, login-loops, redirects die falen) — symptoom dat dieper zit
  • Custom code waarvan oorspronkelijke ontwikkelaar niet meer beschikbaar is
  • Net een migratie gedaan en alle bestanden hebben mogelijk BOM van de transfer-tool gekregen
  • WooCommerce-shop met live klanten waar inloggen of betalen niet meer werkt door deze fout

"Cannot modify header information" lijkt verwarrend, maar 't is een van de meest voorspelbare PHP-fouten. De fouttekst bevat altijd het antwoord, en de oorzaak ligt bijna altijd in een van de zes scenario's hierboven. Lees het bericht, ga naar het bestand op het regelnummer, en je hebt 'm meestal in 5 minuten gefixt.