ob_start and HTTP headers

Fri, Jan 29, 2010 10:00 AM
I was helping someone in IRC deal with some "headers already sent" issues and told them to use ob_start. Very diligently, the person went looking for why that was the right answer. He did not find a good explination. I looked around and I did not either. So, here is why this happens and why ob_start can fix it.

How HTTP works

HTTP is the communication protocol that happens between your web server and the user's browser.  Without too much detail, this is broken into two pieces of data: headers and the body.  The body is the HTML you send. But, before the body is sent, the HTTP headers are sent. Here is an example of an HTTP request response including headers:
HTTP/1.1 200 OK
Date: Fri, 29 Jan 2010 15:30:34 GMT
Server: Apache
X-Powered-By: PHP/5.2.12-pl0-gentoo
Set-Cookie: WCSESSID=xxxxxxxxxxxxxxxxxxxxxxxxxxxx; expires=Sun, 28-Feb-2010 15:30:34 GMT; path=/
Content-Encoding: gzip
Vary: Accept-Encoding
Keep-Alive: timeout=15, max=99
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Ramblings of a web guy</title>
.
.
So, all those lines before the HTML starts have to come first. HTTP headers are where things like cookies and redirection occur. When a PHP script starts to send HTML out to the browser, the headers are stopped and the body begins. When your code tries to set a cookie after this has started, you get the "headers already sent" error message.

How ob_start works

So, how does ob_start help? The ob in ob_start stands for output buffering. ob_start will buffer the output (HTML) until the page is completely done. Once the page is completely done, the headers are sent and then the output is sent. This means any calls to setcookie or the header function will not cause an error and will be sent to the browser properly. You do need to call ob_start before any output occurs. If you start output, it is too late.

The down side

The down side of doing this is that the output is buffered and sent all at once. That means that the time between the user request and the time the first byte gets back to the user is longer than it has to be. However, in modern PHP application design, this is often already the case. An MVC framework for example would do all the data gathering before any presentation is done. So, your application may not have any issue with this.

Another down side is that you (or someone) could get lazy and start throwing setcookie calls in any old place. This should be avoided. It is simply not good programming design. In a perfect world, we would not need output buffering to solve this problem for us.
11 comments
Gravatar for Marat Denenberg

Marat Denenberg Says:

Output buffering should really be used when you need to modify the stream (like compress it or use tidy on html). When you use it this way ... it's sort of like a goto. Which you correctly point out, is not good programming design. Just thinking about this as a solution to writing cookies willy-nilly is giving me a headache. The worst part is that PHP supports multiple levels of output buffering, which in the hands of an idiot can make debugging an absolute horror.

Gravatar for Brian Moon

Brian Moon Says:

the context in which I was helping someone was that they were trying to embed a 3rd party application inside their web site framework. Therefore, they were stuck with this issue. It is not optimal at all.

Gravatar for Fake51

Fake51 Says:

You shouldn't be worried about taking longer to send data to a browser. Most browsers won't start rendering till they have a fairly good chunk of the page to render anyway. Add to that, that you can't control how your server sends data (it might cache small chunks before sending anything off), and you may have even less to gain by trying to output as quickly as possible.

What you should really be worried about when rendering a page is the overall time to render - and here JS and images will most likely take the longest, which output buffering won't change one way or the other.

Gravatar for PHPGangsta

PHPGangsta Says:

I aggree @marat, normally I use output buffering only in cases of html purification or compressing things (if not done via apache or so).
The best way is not to use output buffering, so the first bytes receive the user very fast, and the browser begins to render the page (and other resources like images/hs/css can be downloaded).
In many many cases if I have problems with such setcookie() things or sending of headers late, the better decision is to think about it and rewrite the code.

Gravatar for Bart van Heukelom

Bart van Heukelom Says:

Like you already note yourself, you should just set headers before rendering output. However, there is still a good reason for output buffering, which is the occurring of errors. If halfway rendering the page an error or exception is raised, you generally want to throw away the output you already have and start fresh with rendering an error page. If you've already sent a chunk away to the browser, it's too late for that.

Gravatar for manuel

manuel Says:

it works in linux?, because i run it in windows and works fine, but not in linux ubuntu.

i need anything else?


Thxs

Gravatar for Christian

Christian Says:

Using it to be lazy is no good reason for this tool. Just as in any programming language, you declare your variables at the top of the block, you should have your headers gone before you send your <!DOCTYPE>.

The exception is debugging. If you are using FirePHP, it is very helpful to have the ability to send headers for that at any time, rather than the start.

However, make sure you take it back out before production--exposing critical business secrets to any nutcase with this tool is not my idea of job security.

Gravatar for Kenneth Purtell

Kenneth Purtell Says:

Thanks Brian. Just what I needed.

Add A Comment

Your Name:


Your Email:


Your URL:


Your Comment: