Saturday 18 November 2017

How to fix "Headers already sent" error in PHP

itemprop="text">



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 rel="noreferrer">header() and href="http://php.net/setcookie"
rel="noreferrer">setcookie()
calls.



What could be the reason for this? And
how to fix it?


itemprop="text">
class="normal">Answer



No output
before sending headers!



Functions that
send/modify HTTP headers must be invoked before any output is
made
.
href="https://stackoverflow.com/a/8028979/345031">summary

Otherwise the call
fails:






Warning: Cannot modify header information - headers already sent (output
started at
script:line)




Some
functions modifying the HTTP header
are:





Output can
be:





  • Unintentional:




    • Whitespace
      before or after
      ?>

    • The href="http://en.wikipedia.org/wiki/Byte_order_mark" rel="noreferrer">UTF-8 Byte Order
      Mark specifically

    • Previous error messages or
      notices








  • Intentional:




    • print,
      echo and other functions producing
      output

    • Raw sections
      prior code.





Why
does it happen?



To understand why headers must
be sent before output it's necessary
to look at a typical href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"
rel="noreferrer">HTTP
response. PHP scripts mainly generate HTML
content, but also pass a
set of HTTP/CGI headers to the
webserver:



HTTP/1.1 200
OK
Powered-By: PHP/5.3.7

Vary:
Accept-Encoding
Content-Type: text/html;
charset=utf-8

PHP page output<br /> page

Content


Some more output follows...


and src=internal-icon-delayed>



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,
) it will
flush all
collected headers. Afterwards 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
occured?



The header()
warning contains all relevant information to
locate the problem
cause:






Warning: Cannot modify header information - headers already sent by

(output started at
/www/usr2345/htdocs/auth.php:52) in

/www/usr2345/htdocs/index.php on line
100




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.php
and line
52
. That's where you had to look for premature
output.



Typical
causes:




  1. Print,
    echo



    Intentional output from
    print and echo statements will
    terminate
    the opportunity to send HTTP headers. The application flow
    must

    be restructured to avoid that. Use href="http://php.net/function" rel="noreferrer">functions
    and
    templating schemes. Ensure header() calls occur
    before messages
    are written
    out.



    Functions that produce output
    include




    • print,
      echo, printf,
      vprintf

    • trigger_error,
      ob_flush, ob_end_flush,
      var_dump,
      print_r

    • readfile,
      passthru, flush,
      imagepng,
      imagejpeg




    />among others and user-defined
    functions.


  2. Raw HTML
    areas



    Unparsed HTML sections in a
    .php file are direct output as well.
    Script
    conditions that will trigger a header() call must be
    noted
    before any raw

    blocks.



                html>

    // Too late for headers
    already.


    Use a
    templating scheme to separate processing from output
    logic.




    • Place form
      processing code atop scripts.

    • Use temporary string
      variables to defer messages.

    • The actual output logic and
      intermixed HTML output should follow
      last.



  3. Whitespace
    before for "script.php line
    1
    " warnings



    If the warning
    refers to output in line 1, then
    it's mostly
    leading whitespace, text or HTML
    before the opening
    token.



     #
    There's a SINGLE space/newline before it.



    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.


  4. UTF-8
    BOM




    Linebreaks and spaces alone can
    be a problem. But there are also "invisible"
    character sequences which can
    cause this. Most famously the
    href="http://en.wikipedia.org/wiki/Byte_order_mark"
    rel="noreferrer">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 characters  in 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:



    src="https://i.stack.imgur.com/aXgWY.png" width="590" height="140" alt="joes editor
    showing UTF-8 BOM placeholder, and MC editor a
    dot">



    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
    href="https://stackoverflow.com/questions/3589358/fix-utf8-bom">remedy the
    problem),
    Another option to inspect the BOMs presence is resorting
    to an hexeditor.
    On *nix systems href="http://linux.die.net/man/1/hexdump"
    rel="noreferrer">hexdump is usually
    available,

    if not a graphical variant which simplifies auditing
    these and other issues:



    src="https://i.stack.imgur.com/QyqUr.png" width="560" height="87" alt="beav hexeditor
    showing utf-8 bom">



    An easy fix is to set the
    text editor to save files as "UTF-8 (no BOM)"
    or similar such nomenclature.
    Often newcomers otherwise resort to creating new
    files and just
    copy&pasting the previous code back
    in.



    Correction utilities src="https://i.stack.imgur.com/wnAS9.gif" width="30"
    height="20">




    There are also
    automated tools to examine and rewrite text files
    ( href="https://stackoverflow.com/questions/1068650/using-awk-to-remove-the-byte-order-mark">sed/awk
    or recode).
    For PHP specifically there's the href="http://freshcode.club/projects/phptags"
    rel="noreferrer">phptags tag 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:



    phptags --whitespace
    *.php


    It's sane to use
    on a whole include or project
    directory.



  5. Whitespace
    after ?>



    If the
    error source is mentioned as behind the
    href="https://stackoverflow.com/questions/4410704/php-closing-tag">closing
    ?>
    then this is where some whitespace or
    raw text got written out.
    The PHP end marker does not terminate script
    executation 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
    ?> PHP

    close tags should be omitted.
    This eschews a small portion of these cases.
    (Quite
    commonly include()d scripts are the
    culprit.)


  6. Error source mentioned as
    "Unknown on line 0"



    It's typically a PHP
    extension or php.ini setting if no error source
    is
    concretized.




    • It's
      occasionally the gzip stream encoding setting
      href="https://stackoverflow.com/questions/622192/php-warning-headers-already-sent-in-unknown">or
      the
      ob_gzhandler.


    • But
      it could also be any doubly loaded extension=
      module
      generating an implicit PHP startup/warning
      message.


  7. Preceding
    error messages



    If another PHP statement or
    expression causes a warning message or
    notice being printeded 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.

    rel="noreferrer">isset() or href="http://php.net/@" rel="noreferrer">@()
    -
    when either doesn't obstruct debugging later
    on.




No error
message



If you have
error_reporting or display_errors
disabled per php.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
very
advisable to probe for warnings. Reenable them with two simple
commands
atop the invocation
script:



error_reporting(E_ALL);
ini_set("display_errors",
1);


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:



exit(header("Location:
/finished.html"));


Preferrably
even a utility function, which prints a user message
in case of
header()
failures.



Output buffering as
workaround




PHPs href="http://www.php.net/manual/en/intro.outcontrol.php" rel="noreferrer">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.




  1. The
    rel="noreferrer">output_buffering=
    setting
    nevertheless can help.
    Configure it in the href="http://www.php.net/manual/en/configuration.file.php"
    rel="noreferrer">php.ini

    or via href="http://www.php.net/manual/en/configuration.changes.php"
    rel="noreferrer">.htaccess
    or even href="http://php.net/manual/en/configuration.file.per-user.php"
    rel="noreferrer">.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.


  2. It can likewise be engaged
    with a call to rel="noreferrer">ob_start();
    atop the
    invocation script. Which however is less reliable for multiple
    reasons:




    • Even if
      starts the first script, whitespace
      or a
      BOM might get shuffled before, href="https://stackoverflow.com/questions/2168956/php-header-problem-even-i-use-ob-start-and-ob-end-flush">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 furher
      workaround.)


    • The buffer is limited in
      size, and can easily overrun when left to defaults.
      And that's not a rare
      occurence either, href="https://stackoverflow.com/questions/17643837/php-headers-already-sent-error-depending-on-output-length">difficult
      to track down
      when it
      happens.






Both
approaches therefore may become unreliable - in particular when switching
between
development setups and/or production servers. Which is why output
buffering is
widely considered just a crutch / strictly a
workaround.



See also the href="http://www.php.net/manual/en/outcontrol.examples.basic.php"
rel="noreferrer">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 href="http://php.net/manual/en/outcontrol.configuration.php" rel="noreferrer">output
buffering
php.ini setting
has changed. It's likely
unconfigured on the current/new
server.



Checking with
headers_sent()



You can
always use rel="noreferrer">headers_sent() to probe
if
it's still possible to... send headers. Which is useful to conditionally
print
an info or apply other fallback
logic.




if
(headers_sent()) {
die("Redirect failed. Please click on this link: href=...>");
}
else{
exit(header("Location:
/user.php"));
}


Useful
fallback workarounds
are:





  • HTML
    tag



    If
    your application is structurally hard to fix, then an easy (but
    somewhat
    unprofessional) way to allow redirects is injecting a
    HTML
    tag. A redirect can be achieved
    with:



                 http-equiv="Location"
    content="http://example.com/">



    Or
    with a short delay:



                 http-equiv="Refresh" content="2;
    url=../target.html">


    This
    leads to non-valid HTML when utilized past the
    section.
    Most browsers still accept
    it.


  • JavaScript
    redirect




    As alternative a href="https://stackoverflow.com/questions/503093/how-can-i-make-a-redirect-page-in-jquery-javascript">JavaScript
    redirect
    can be used for page
    redirects:



     


    While
    this is often more HTML compliant than the
    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 href="http://php.net/http_redirect"
rel="noreferrer">http_redirect()
PECL extension
does.)



Why setcookie()
and session_start() are also
affected



Both
setcookie() and session_start() need
to send a Set-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




No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...