I'm intermittently getting this issue in my PHP scripts (PHP 5.6, Apache 2.2):
Warning: Cannot modify header information - headers already sent in /path/to/index.php on line 55
This warning doesn't have the "sent by" portion that I've seen in other questions about this, so I added this code just before the offending header()
and setcookie()
calls:
if (headers_sent($filename, $linenum)){
echo("Output buffer: #" . ob_get_contents() . "#");
echo "Headers already sent in $filename on line $linenum: ";
print_r(headers_list());
}
Here's the output I got when the issue occurred:
Output buffer: ##
Headers already sent in on line 0:
Array (
[0] => X-Powered-By: PHP/5.6.23
[1] => Content-type: text/html; charset=UTF-8
)
(Side note: I have output_buffering set to 4096 bytes in php.ini, so shouldn't the 63 characters in these two headers be buffered up and waiting for more, rather than being sent prematurely?)
This issue arises the first time I spin up a Docker container containing the webserver. After that, it only (but not always) occurs when I access my site for the first time in a while (maybe one or two hours), when I call header()
and setcookie()
to log a user in or to redirect to the login page.
I've read and reread this answer to the general "Headers already sent" error, and, to the best of my ability, I've ruled out these possible causes :
- HTML blocks or calls to
print
,echo
, etc. before my calls tosetcookie()
orheader()
- whitespace outside my PHP tags
- BOMs
auto_prepend_file
php.ini settinggzip
stream encoding - zlib is installed, butzlib.output_compression
is Off- Duplicate
extension=
php.ini settings
That answer mentions that
It's typically a PHP extension or php.ini setting if no error source is concretized.
So, I'm now looking through my extensions... get_loaded_extensions
gives me a 51-length Array with these entries:
Core, date, ereg, libxml, openssl,
pcre, zlib, filter, hash, Reflection,
SPL, session, standard, apache2handler, bz2,
calendar, ctype, curl, dom, exif,
fileinfo, ftp, gd, gettext, iconv,
mysqlnd, PDO, Phar, posix, shmop,
SimpleXML, snmp, soap, sockets, sqlite3,
sysvmsg, sysvsem, sysvshm, tokenizer, xml,
xmlwriter, xsl, mysql, mysqli, pdo_mysql,
pdo_sqlite, wddx, xmlreader, json, zip, mhash
I'm not using all of these, so I plan to go through and remove the unused ones, and hope one of those was causing the problem.
Worst-case scenario, I'll try bumping my output_buffering
value or use ob_start()
and ob_end_flush()
to the starts and ends of my files. I don't know why this would fix it when my current output_buffering
value of 4096 doesn't, and I understand that this workaround comes with its own issues.
What am I missing here—are there other possible causes I need to check for? Should I try a different PHP version, or perhaps run a subset of my code on a clean PHP install with no extensions?
EDIT: Added the ob_get_contents()
call and output, and information about being able to consistently reproduce this by spinning up new Docker containers. Removed info about my error_reporting
value; changing this only uncovered an always_populate_raw_post_data
deprecation notice, fixing which had no effect on the issue described here.
index.php
? If it's included, then please provide the whole include chain sources including the index file. – Gifferdob_start()
at the very beginning of your entry point script? That's not clear from your question but without thatob_get_contents()
won't work. Also, try to usevar_dump(ob_get_contents())
. I think it's better than simply echoing the values because it'll say the length of the string. – Whoresonalways_populate_raw_post_data
thing didn't solve the issue? That's certainly a cause for it. On another note - looking at successfully loaded extensions won't help; chances are that error would be triggered for the opposite - a failure to load an extension. If it says line 0, you can be certain that it has nothing to do with the code, but something triggered while PHP itself is initialized. – Finnyob_get_contents()
would work with myoutput_buffering
setting in php.ini. Maybe I'm fundamentally misunderstanding output buffering. – Oratoralways_populate_raw_post_data = -1
. Thanks for the tip about extensions; I'll look for ways to identify any extensions that failed to load (but I'd expect to see logs to that effect). – Orator