PHP Output Buffering: ob_start(), Capture and Flush Output

Output buffering is one of those PHP features you may ignore for years, until you need to capture output from an included file or stop some HTML from reaching the browser too early.

Instead of sending output immediately, PHP can hold it in an internal buffer. Your code can then read, modify, discard or release that output when needed.

The basic pattern is simple:

<?php

ob_start();

echo 'Hello from the buffer';

$output = ob_get_clean();

echo $output;

ob_start() starts a new output buffer. The echo output goes into that buffer instead of directly to the next output layer. Then ob_get_clean() returns the buffered content as a string and closes the buffer.

What is PHP output buffering?

PHP output buffering temporarily stores generated output before passing it to the next output layer.

The buffered output can come from echo, print, included PHP templates, or HTML written outside PHP tags.

For example:

<?php

ob_start();

echo '<h1>Profile</h1>';
echo '<p>Welcome, John.</p>';

$html = ob_get_clean();

echo $html;

Here, the two echo statements do not immediately pass their content to the next output layer. PHP first stores them in the active buffer. ob_get_clean() then gives us the complete HTML as a string.

That makes output buffering especially useful when code produces output directly but you need that output as data. It is particularly handy when working with PHP include and require statements that render template files.

When is output buffering useful?

In practical PHP applications, output buffering is commonly useful for:

  • capturing the rendered output of a PHP template or included file;
  • modifying generated HTML before sending it;
  • discarding output when an operation fails;
  • controlling when generated output moves to the next output layer;
  • working with older functions or libraries that print output instead of returning it.

One important distinction is that output buffering controls PHP output. It does not guarantee that a browser will display flushed content immediately. Web servers, proxies and browsers can have their own buffering layers.

How ob_start() works

ob_start() creates a new output buffer. After that, normal PHP output is stored in the buffer until you read, clean or end it.

<?php

ob_start();

echo 'First line';
echo 'Second line';

$content = ob_get_contents();

echo $content;

ob_end_clean();

ob_get_contents() returns the current contents of the buffer without closing it. The buffer remains active until ob_end_clean() removes it.

Be careful with the example above. The echo $content statement also writes into the active buffer because buffering is still enabled. If your goal is simply to capture the output and close the buffer, ob_get_clean() is usually clearer.

Get buffered output with ob_get_clean()

For most capture-and-return cases, ob_get_clean() gets the current output buffer and turns it off. It combines two operations:

  • gets the current buffer contents;
  • removes the active buffer.
<?php

ob_start();

echo '<p>Buffered content</p>';

$content = ob_get_clean();

echo $content;

This pattern is useful because the captured content becomes a normal PHP string. You can store it, modify it, return it from a function or pass it to another part of your application.

Capture the output of a PHP template

A common real-world use of output buffering is rendering a PHP template into a variable.

Suppose profile.php contains this template:

<h1><?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?></h1>
<p>Member profile</p>

You can capture the complete rendered HTML like this:

<?php

function renderProfile(string $name): string
{
    ob_start();

    include 'profile.php';

    return ob_get_clean();
}

$html = renderProfile('John');

echo $html;

The included file renders normally, but its output is captured instead of being sent onward immediately.

This technique is useful for small template systems, email HTML, reusable page fragments and legacy PHP files that were written to print their output directly.

It also keeps the template simple. The template can use ordinary PHP and HTML without manually concatenating a large HTML string.

Clean, flush, or end an output buffer

PHP provides several functions for controlling the active buffer. The names are similar, so it helps to separate them by intent.

Discard buffered output with ob_clean()

ob_clean() removes the current buffer contents but keeps the buffer active.

<?php

ob_start();

echo 'This will be discarded';

ob_clean();

echo 'This remains in the buffer';

$output = ob_get_clean();

echo $output;

This is useful when you want to reset the current buffered output and continue buffering.

Send buffered output with ob_flush()

ob_flush() sends the current output buffer onward and keeps the buffer active. The buffered contents are discarded after they are flushed.

<?php

ob_start();

echo 'Processing started...';

ob_flush();

echo 'Processing completed.';

ob_end_flush();

ob_end_flush() is similar, but it also closes the active buffer after sending its contents onward.

Discard and close with ob_end_clean()

Use ob_end_clean() when you no longer need the buffered output and want to close the buffer completely.

<?php

ob_start();

echo 'Temporary output';

ob_end_clean();

echo 'Only this output remains.';

ob_get_contents() vs ob_get_clean()

The difference is simple but important.

ob_get_contents() reads the current buffer and leaves it open. ob_get_clean() reads the buffer and closes it.

<?php

ob_start();

echo 'Example';

$content = ob_get_contents();

// The buffer is still active here.
ob_end_clean();

If you only need to capture a block of output once, ob_get_clean() is usually the cleaner choice:

<?php

ob_start();

echo 'Example';

$content = ob_get_clean();

Using the combined function also reduces the chance of accidentally leaving an output buffer open.

Nested output buffers

PHP can have more than one output buffer active at the same time. Each call to ob_start() adds another buffer level.

<?php

ob_start();

echo 'Outer buffer';

ob_start();

echo 'Inner buffer';

$inner = ob_get_clean();

echo ' | Captured: ' . $inner;

$output = ob_get_clean();

echo $output;

The inner buffer is handled first. After it is closed, output continues into the outer buffer.

Nested buffers can be useful in template systems and libraries, but they can also make output flow harder to follow. If you do not need multiple levels, keep the buffering logic simple.

Check the current output buffer level

ob_get_level() tells you how many output buffers are currently active.

<?php

echo ob_get_level(); // 0

ob_start();

echo ob_get_level(); // 1

ob_start();

echo ob_get_level(); // 2

ob_end_clean();
ob_end_clean();

This is especially useful when debugging code that starts buffers in several places.

Use try and finally when capturing template output

If an exception occurs while a buffer is open, you should make sure the buffer is cleaned up. Otherwise, later output may behave in surprising ways.

<?php

function renderTemplate(string $file, array $data = []): string
{
    extract($data, EXTR_SKIP);

    ob_start();

    try {
        include $file;

        return ob_get_clean();
    } catch (Throwable $exception) {
        ob_end_clean();

        throw $exception;
    }
}

The important part is not the exact helper function. It is the cleanup. If rendering fails, close the buffer before rethrowing the exception.

For larger applications, template engines usually handle this internally. But for small PHP projects, this pattern is reliable and easy to understand.

Output buffering and headers

Output buffering can also help when PHP needs to send HTTP headers after some output-producing code has already run.

Without buffering, output sent too early can lead to the familiar headers already sent warning in PHP.

<?php

ob_start();

echo 'Preparing response...';

header('X-App-Status: ready');

$content = ob_get_clean();

echo $content;

Because the output is still buffered, PHP has not yet passed that content to the next output layer when header() runs.

Still, output buffering should not be used to hide poor response flow. In most applications, send headers before rendering page output. Buffering is useful when you deliberately need to capture or control output, not as a permanent fix for misplaced echo statements.

Output buffering does not guarantee instant browser output

A common assumption is that ob_flush() or flush() will immediately make partial output appear in the browser.

That is not guaranteed.

PHP may release its own buffered data, while another layer still holds it. The PHP manual notes that flush() cannot necessarily override web-server buffering and has no effect on browser-side buffering. Depending on the setup, buffering may also happen in:

  • PHP configuration;
  • the web server;
  • a reverse proxy;
  • compression middleware;
  • the browser itself.

So this code may not visibly update the page immediately:

<?php

ob_start();

echo 'Step 1 complete';
ob_flush();
flush();

sleep(2);

echo 'Step 2 complete';

ob_end_flush();

The functions tell PHP to move available output forward, but they cannot force every later layer to display it immediately.

Common mistakes with PHP output buffering

Leaving a buffer open by accident

If you call ob_start(), make sure the buffer is eventually closed or intentionally flushed.

For simple capture operations, this is another reason to prefer:

<?php

$content = ob_get_clean();

over separate calls when you do not need to keep the buffer active.

Echoing while the buffer is still active

This can be confusing:

<?php

ob_start();

echo 'Hello';

$content = ob_get_contents();

echo $content;

The second echo does not bypass the buffer. It is added to the same active buffer.

Using buffering everywhere

Output buffering is useful, but it should have a clear purpose. If normal code can return a string instead of printing it, returning the string is often simpler.

Use buffering when you need to capture output that is naturally produced as output, such as an included PHP template or existing rendering code.

PHP output buffering functions at a glance

These are the functions you will use most often:

  • ob_start() starts a new output buffer.
  • ob_get_contents() returns the current buffer contents without closing it.
  • ob_get_clean() returns the current buffer contents and closes the buffer.
  • ob_clean() clears the current buffer but keeps it active.
  • ob_flush() sends the current buffer contents onward and keeps the buffer active.
  • ob_end_clean() discards the current buffer and closes it.
  • ob_end_flush() sends the current buffer contents onward and closes it.
  • ob_get_level() returns the number of active output-buffer levels.

For most application code, you will probably use ob_start() with ob_get_clean(). The other functions become useful when you need finer control over whether buffered output is kept, discarded or sent onward.

When should you use output buffering?

Use output buffering when you genuinely need to capture or control generated output.

It can also be useful in techniques such as caching dynamically generated PHP pages, where the generated response is captured before it is stored or sent.

A good example is rendering an existing PHP template into a string:

<?php

ob_start();

include 'invoice-template.php';

$html = ob_get_clean();

sendInvoiceEmail($html);

Without buffering, the template would print directly. With buffering, the rendered HTML becomes reusable data.

On the other hand, if you are writing a normal function from scratch, prefer returning a value directly when possible:

<?php

function getMessage(string $name): string
{
    return 'Hello, ' . $name;
}

That is simpler than starting a buffer just to capture an echo.

Conclusion

PHP output buffering gives you control over output that would otherwise be sent immediately to the next output layer.

The most practical pattern is straightforward: start buffering with ob_start(), generate the output, then capture and close the buffer with ob_get_clean().

It is especially useful for PHP templates, legacy rendering code and situations where generated HTML needs to become a string before it is used elsewhere.

Keep buffering local and intentional. When normal return values can solve the problem more clearly, use them. When you genuinely need to capture printed output, output buffering is one of PHP’s simplest and most useful built-in tools.

Photo of Vincy, PHP developer
Written by Vincy Last updated: August 14, 2026
I'm a PHP developer with 20+ years of experience and a Master's degree in Computer Science. I build and improve production PHP systems for eCommerce, payments, webhooks, and integrations, including legacy upgrades (PHP 5/7 to PHP 8.x).

Continue Learning

These related tutorials may help you continue learning.

8 Comments on "PHP Output Buffering: ob_start(), Capture and Flush Output"

  • Rosa Maria says:

    Hi Vincy,
    Im web developer at uOttawa
    Nice to see women taking place in It world

    Best regards!

  • jaikishan kumar says:

    hi vincy you are doing great Job!

  • capenter says:

    I have a situation that by the end of the month when my MySQL table has at least 140 rows, I cannot update the table from php using a smartphone, ipad or tablet, I can just update it from my laptop. I am wodering if it has something to do with the buffering or with the browser. I am trying to turn on the output buffering but I haven’t been able to .
    I have the ob_start() at the beginning of the table but when I check the output buffering it says no value.

    • Vincy says:

      This is unlikely to be related to PHP output buffering. `ob_start()` controls the generated page output, not whether a MySQL `UPDATE` succeeds.

      Since it works on a laptop but fails on mobile devices when the table becomes larger, I would first check the browser developer/network logs and the PHP/MySQL error logs. Also check whether the mobile form is submitting all expected values and whether PHP limits such as `max_input_vars`, `post_max_size`, or request size are being reached.

      Try logging the SQL error and the submitted POST data when the update fails. That should help identify whether the problem is in the request, PHP code, or MySQL.

  • Jairo says:

    Hi Vincy, this is the best article on Net. Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *

Explore topics
Need PHP help?