When running my script, I am getting several errors like this:
Warning: Cannot modify header information – headers already sent by (output started at /some/file.php:12) in /some/file.php on line 23
The lines mentioned in the error messages contain header() and setcookie() calls.
What could be the reason for this? And how to fix it?
No output before sending headers!
Functions that send/modify HTTP headers must be invoked before any output is made.
summary ⇊
Otherwise the call fails:
Some functions modifying the HTTP header are:
header/header_removesession_start/session_regenerate_idsetcookie/setrawcookieOutput can be:
Unintentional:
<?phpor after?>Intentional:
print,echoand other functions producing output<html>sections prior<?phpcode.Why does it happen?
To understand why headers must be sent before output it’s necessary
to look at a typical HTTP
response. PHP scripts mainly generate HTML content, but also pass a
set of HTTP/CGI headers to the webserver:
The page/output always follows the headers. PHP has to pass the
headers to the webserver first. It can only do that once.
After the double linebreak it can nevermore amend them.
When PHP receives the first output (
print,echo,<html>) it willflush all collected headers. Afterward it can send all the output
it wants. But sending further HTTP headers is impossible then.
How can you find out where the premature output occurred?
The
header()warning contains all relevant information tolocate the problem cause:
Here "line 100" refers to the script where the
header()invocation failed.The "output started at" note within the parenthesis is more significant.
It denominates the source of previous output. In this example, it’s
auth.phpand line
52. That’s where you had to look for premature output.Typical causes:
Print, echo
Intentional output from
printandechostatements will terminate the opportunity to send HTTP headers. The application flow must be restructured to avoid that. Use functionsand templating schemes. Ensure
header()calls occur before messagesare written out.
Functions that produce output include
print,echo,printf,vprintftrigger_error,ob_flush,ob_end_flush,var_dump,print_rreadfile,passthru,flush,imagepng,imagejpegamong others and user-defined functions.
Raw HTML areas
Unparsed HTML sections in a
.phpfile are direct output as well.Script conditions that will trigger a
header()call must be notedbefore any raw
<html>blocks.Use a templating scheme to separate processing from output logic.
Whitespace before
<?phpfor "script.php line 1" warningsIf the warning refers to output inline
1, then it’s mostlyleading whitespace, text or HTML before the opening
<?phptoken.Similarly it can occur for appended scripts or script sections:
PHP actually eats up a single linebreak after close tags. But it won’t
compensate multiple newlines or tabs or spaces shifted into such gaps.
UTF-8 BOM
Linebreaks and spaces alone can be a problem. But there are also "invisible"
character sequences that can cause this. Most famously the
UTF-8 BOM (Byte-Order-Mark)
which isn’t displayed by most text editors. It’s the byte sequence
EF BB BF, which is optional and redundant for UTF-8 encoded documents. PHP however has to treat it as raw output. It may show up as the charactersin the output (if the client interprets the document as Latin-1) or similar "garbage".In particular graphical editors and Java-based IDEs are oblivious to its
presence. They don’t visualize it (obliged by the Unicode standard).
Most programmer and console editors however do:
There it’s easy to recognize the problem early on. Other editors may identify
its presence in a file/settings menu (Notepad++ on Windows can identify and
remedy the problem),
Another option to inspect the BOMs presence is resorting to an hexeditor.
On *nix systems
hexdumpis usually available,if not a graphical variant which simplifies auditing these and other issues:
An easy fix is to set the text editor to save files as "UTF-8 (no BOM)"
or similar to such nomenclature. Often newcomers otherwise resort to creating new files and just copy&pasting the previous code back in.
Correction utilities
There are also automated tools to examine and rewrite text files
(
sed/awkorrecode).For PHP specifically there’s the
phptagstag tidier.It rewrites close and open tags into long and short forms, but also easily
fixes leading and trailing whitespace, Unicode and UTF-x BOM issues:
It’s safe to use on a whole include or project directory.
Whitespace after
?>If the error source is mentioned as behind the
closing
?>then this is where some whitespace or the raw text got written out.
The PHP end marker does not terminate script execution at this point. Any text/space characters after it will be written out as page content
still.
It’s commonly advised, in particular to newcomers, that trailing
?>PHPclose tags should be omitted. This eschews a small portion of these cases.
(Quite commonly
include()dscripts are the culprit.)Error source mentioned as "Unknown on line 0"
It’s typically a PHP extension or php.ini setting if no error source
is concretized.
gzipstream encoding settingor the
ob_gzhandler.extension=modulegenerating an implicit PHP startup/warning message.
Preceding error messages
If another PHP statement or expression causes a warning message or
notice being printed out, that also counts as premature output.
In this case you need to eschew the error,
delay the statement execution, or suppress the message with e.g.
isset()or@()–when either doesn’t obstruct debugging later on.
No error message
If you have
error_reportingordisplay_errorsdisabled perphp.ini,then no warning will show up. But ignoring errors won’t make the problem go
away. Headers still can’t be sent after premature output.
So when
header("Location: ...")redirects silently fail it’s veryadvisable to probe for warnings. Reenable them with two simple commands
atop the invocation script:
Or
set_error_handler("var_dump");if all else fails.Speaking of redirect headers, you should often use an idiom like
this for final code paths:
Preferably even a utility function, which prints a user message
in case of
header()failures.Output buffering as a workaround
PHPs output buffering
is a workaround to alleviate this issue. It often works reliably, but shouldn’t
substitute for proper application structuring and separating output from control
logic. Its actual purpose is minimizing chunked transfers to the webserver.
The
output_buffering=setting nevertheless can help.
Configure it in the php.ini
or via .htaccess
or even .user.ini on
modern FPM/FastCGI setups.
Enabling it will allow PHP to buffer output instead of passing it to the webserver instantly. PHP thus can aggregate HTTP headers.
It can likewise be engaged with a call to
ob_start();atop the invocation script. Which however is less reliable for multiple reasons:
Even if
<?php ob_start(); ?>starts the first script, whitespace or aBOM might get shuffled before, rendering it ineffective.
It can conceal whitespace for HTML output. But as soon as the application logic attempts to send binary content (a generated image for example),
the buffered extraneous output becomes a problem. (Necessitating
ob_clean()as a further workaround.)
The buffer is limited in size, and can easily overrun when left to defaults.
And that’s not a rare occurrence either, difficult to track down
when it happens.
Both approaches therefore may become unreliable – in particular when switching between
development setups and/or production servers. This is why output buffering is
widely considered just a crutch / strictly a workaround.
See also the basic usage example
in the manual, and for more pros and cons:
But it worked on the other server!?
If you didn’t get the headers warning before, then the output buffering
php.ini setting
has changed. It’s likely unconfigured on the current/new server.
Checking with
headers_sent()You can always use
headers_sent()to probe ifit’s still possible to… send headers. Which is useful to conditionally print
info or apply other fallback logic.
Useful fallback workarounds are:
HTML
<meta>tagIf your application is structurally hard to fix, then an easy (but
somewhat unprofessional) way to allow redirects is injecting a HTML
<meta>tag. A redirect can be achieved with:Or with a short delay:
This leads to non-valid HTML when utilized past the
<head>section.Most browsers still accept it.
JavaScript redirect
As alternative a JavaScript redirect
can be used for page redirects:
While this is often more HTML compliant than the
<meta>workaround,it incurs a reliance on JavaScript-capable clients.
Both approaches however make acceptable fallbacks when genuine HTTP header()
calls fail. Ideally you’d always combine this with a user-friendly message and
clickable link as last resort. (Which for instance is what the http_redirect()
PECL extension does.)
Why
setcookie()andsession_start()are also affectedBoth
setcookie()andsession_start()need to send aSet-Cookie:HTTP header.The same conditions therefore apply, and similar error messages will be generated
for premature output situations.
(Of course, they’re furthermore affected by disabled cookies in the browser
or even proxy issues. The session functionality obviously also depends on free
disk space and other php.ini settings, etc.)
Further links
It covers HTTP in detail and gives a few guidelines for rewriting scripts.