PHP: Headers already sent error

Written on

In PHP, sometimes when you're writing code you'll get an error message like this one:

Warning: Cannot modify header information - headers already sent

It's followed by "output started at" and a filename and line number. This tends to happen when using setcookie(), session_start() or header().

Why does this happen?

To understand why this is happening we'll have to learn how HTTP works. HTTP is the language that browsers and servers use to communicate with each other. In HTTP, messages are sent between the browser (formally referred to as the "client") and the server. When somebody opens a web page, the client sends an HTTP request and the browser gives an HTTP response. This is an HTTP transaction. We refer to the request and response as messages.

An HTTP message has two parts:

  1. Headers
  2. Body

The headers contain information about the content being sent, such as what file type the content is and how much content there is, and the body contains the content itself. Requests tend to be short and have little or no body content. Responses generally have a lot of content in the body.

When PHP runs, it tries to send information to the browser as quickly as possible, so as soon as something is ready it immediately sends it. The problem you're getting occurs due to this. The moment that any body content is generated, PHP immediately sends all the headers. At this point if you try to send another header a warning will appear and the header will be ignored. You can choose to suppress the warning, but the problem of the header not being sent still exists.

How do I fix it?

Body content is generated as soon as an echo statement is sent or anything outside PHP tags exists. This is the point at which headers can't be sent anymore.

PHP's error message is really helpful because it points you to the specific line of code where the output started. Look for at the line of code referenced by the "output started at" message. You have to make sure that any calls to setcookie(), session_start() or header() are placed before that line of code.

What if I need the content before that line?

If you cannot move the echo statement or other content after the line that sends the header you can use output buffering. Here's a little example on how to use output buffering:

<?php
// ...
ob_start();
?>
Here's some content outside PHP tags
<?php
echo 'An echo statement works too';
$content = ob_get_clean();
?>

The variable $content now contains the content that was going to sent to the browser, you can choose to print $content further ahead in the code. The buffering starts with the ob_start() function call and ends with ob_get_clean() which stores the content into the variable.

What if the line that the output started at is not an "echo" statement or non-PHP content?

This happens when a function shows a warning or error of its own. Make sure to correct that error and the "headers already sent" error will get fixed too.

The error is pointing to line 1 but there's nothing there, what should I do?

There are two possible reasons for that:

  1. Your code has spaces, tabs or line breaks before the opening <?php tag
  2. Your document has a byte-order-mark

For the first case, in your text editor, put the cursor at the beginning and press backspace and delete until you've made sure that no whitespace is there.

What's a byte-order-mark? It's a few bytes at the beginning of a file that indicates the order of bytes for multibyte characters in a UTF-8 encoded document. If that sounds complicated, you can learn more about that by studying UTF-8, but in short, your document has three invisible letters at the very beginning. To remove them, look for the encoding options in your text editor and make sure it's saving as UTF-8 without BOM. In Windows Notepad, the encoding option is right in the "Save As..." menu, but if you're using Notepad, I would recommend switching to an editor that's better suited for coding, like Notepad++.

I hope this helped. If you have any further questions, just send me a message!