Question
I am running a PHP script and getting warnings 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 warning contain calls such as header() and setcookie().
What causes this error in PHP, and how can it be fixed correctly?
Short Answer
By the end of this page, you will understand what HTTP headers are, why PHP must send them before any body output, what triggers the Headers already sent warning, and how to fix it in practical ways. You will also see common causes such as accidental whitespace, echo output, included files, UTF-8 BOM issues, and debugging prints before header() or setcookie().
Concept
In PHP, functions like header(), setcookie(), and session_start() work by sending HTTP headers to the browser. Headers are metadata sent before the actual page content.
Once PHP has started sending output to the browser—such as HTML, text, whitespace, or anything printed with echo—the headers are considered sent. After that point, PHP can no longer change them, so you get this warning:
Cannot modify header information - headers already sent
Why this matters
Headers control important behavior, such as:
- redirects with
header('Location: ...') - cookies with
setcookie() - sessions with
session_start() - content type with
header('Content-Type: application/json') - cache settings and download responses
If output happens too early, PHP loses the ability to send those instructions properly.
What counts as output?
Any of the following can trigger the problem:
echo,print,var_dump()
Mental Model
Think of an HTTP response like mailing a package:
- Headers are the shipping label and instructions.
- Output is the actual item inside the box.
Once you seal and ship the package, you cannot go back and change the shipping label. In the same way, once PHP starts sending page content, it cannot change headers anymore.
So the rule is simple:
- send all header-related instructions first
- send page content after that
Syntax and Examples
The most common header-related functions are:
header('Location: /login.php');
setcookie('theme', 'dark', time() + 3600);
session_start();
These must happen before output.
Broken example
<?php
echo "Welcome";
header('Location: /dashboard.php');
This fails because echo "Welcome"; sends output before the redirect header.
Correct example
<?php
header('Location: /dashboard.php');
exit;
Here, the header is sent before any output.
Another broken example with HTML
<html>
<body>
<?php
setcookie('user', 'alice', time() + );
</body>
</html>
Step by Step Execution
Consider this code:
<?php
require 'init.php';
echo "Loading...";
header('Location: /home.php');
exit;
Now trace it step by step:
- PHP starts running the script.
require 'init.php';includes another file.- If
init.phpprints anything, output may already begin here. echo "Loading...";definitely sends output to the browser.header('Location: /home.php');now tries to send a redirect header.- PHP detects that output has already started.
- PHP raises the
Cannot modify header information - headers already sentwarning. - The redirect may fail or behave unexpectedly.
Fixed version
<?php
require 'init.php';
header('Location: /home.php');
exit;
Another useful trace
();
(, );
;
Real World Use Cases
This concept appears often in real PHP applications.
Redirecting after login
<?php
if ($loginSuccessful) {
header('Location: /dashboard.php');
exit;
}
If any debug output appears before this, the redirect can fail.
Setting cookies for user preferences
<?php
setcookie('language', 'en', time() + 86400 * 30);
This must happen before HTML output.
Starting sessions
<?php
session_start();
$_SESSION['user_id'] = 42;
If output starts too early, session cookies may not be sent.
Serving JSON from an API endpoint
<?php
header('Content-Type: application/json');
echo json_encode([ => ]);
Real Codebase Usage
In real projects, developers avoid this error by structuring code so response decisions happen early.
Common patterns
Guard clauses and early redirects
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: /login.php');
exit;
}
Check conditions early, then redirect before rendering anything.
Separate logic from templates
A common structure is:
- bootstrap/config loads first
- request handling and validation run next
- headers, redirects, or cookies are sent
- HTML template is rendered last
Validation before output
<?php
$token = $_GET['token'] ?? '';
if ($token === '') {
header('Location: /error.php');
exit;
}
Avoid debug output in production flow
Debugging with var_dump() before redirects is a common cause of this error.
Common Mistakes
1. Printing before calling header()
Broken:
<?php
echo "Redirecting...";
header('Location: /home.php');
Fix:
<?php
header('Location: /home.php');
exit;
2. Whitespace before <?php
Broken:
<?php
header('Location: /home.php');
That blank space may count as output.
Fix: remove all characters before the opening <?php tag.
3. Trailing whitespace after ?>
Broken:
<?php
$config = ['debug' => true];
?>
If this file is included, trailing spaces may cause output.
Comparisons
| Concept | What it does | Must happen before output? | Notes |
|---|---|---|---|
header() | Sends raw HTTP headers | Yes | Used for redirects, content type, cache control |
setcookie() | Sends a Set-Cookie header | Yes | Cookies are part of headers |
session_start() | Starts session handling and usually sends a cookie | Yes | Often fails if output started already |
echo / print | Sends response body content | No | But once used, headers are usually locked |
| HTML outside PHP |
Cheat Sheet
Core rule
Call these before any output:
header()setcookie()session_start()
Common causes of Headers already sent
echo,print,var_dump()before headers- HTML before PHP header calls
- whitespace before
<?php - trailing spaces after
?> - included files with accidental output
- UTF-8 BOM
- warnings/notices printed first
Safe redirect pattern
<?php
header('Location: /target.php');
exit;
Safe JSON response pattern
<?php
header('Content-Type: application/json');
echo json_encode(['ok' => ]);
FAQ
Why does PHP say headers were already sent?
Because some output reached the browser before PHP tried to send a header such as a redirect, cookie, or session header.
Does whitespace really count as output in PHP?
Yes. Spaces, blank lines, or characters outside PHP tags can be sent as output.
Why does the warning mention two different line numbers?
One line shows where output started. The other shows where PHP later tried to send the header.
Can setcookie() cause the same error as header()?
Yes. Cookies are sent through HTTP headers, so they must also be set before output.
Does session_start() need to be called before output?
Yes. It often sends a session cookie header, so it must run before body output.
Can I use output buffering to fix this?
Sometimes. ob_start() can delay output, but it is usually better to remove the early output and structure the script correctly.
Should I close PHP files with ?>?
In pure PHP files, it is usually better not to. This helps prevent accidental trailing whitespace.
Can warnings or notices trigger this problem?
Yes. If errors are displayed to the browser before a header call, they can count as output.
Mini Project
Description
Build a small login gate script that checks whether a user is logged in. If not, it redirects to a login page. This project demonstrates the correct order of session handling, header sending, and output rendering in a practical PHP flow.
Goal
Create a PHP page that safely redirects unauthenticated users before any output is sent.
Requirements
[ "Start the session at the top of the script.", "Check whether a user ID exists in the session.", "Redirect to /login.php if the user is not logged in.", "Stop script execution after the redirect.", "Only render HTML if the user is authenticated." ]
Keep learning
Related questions
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.
Convert a PHP Object to an Associative Array
Learn how to convert a PHP object to an associative array, including quick methods, recursion, pitfalls, and practical examples.
Convert a Postman Request to cURL and PHP cURL
Learn how to convert a Postman POST request into a cURL command and use the same request in PHP cURL with headers and body.