PHP Development

Navigating PHP Session and Header Errors: A Practical Guide

By the Domain India teamPublished 6 min read
Knowledge base article
Contents (10 sections)

"Headers already sent" is one of the most common PHP warnings, and it breaks sessions, logins and redirects. It always means the same thing: your script sent some output to the browser before it tried to send a header. The warning tells you exactly where that output started, so the fix is usually a one-line change once you know how to read it.

Key takeaways

PHP must send headers (cookies, redirects, session_start()) before any output. Read the "output started at file:line" part of the warning, go to that file and line, and remove what is printed there: HTML before <?php, a blank line or space outside the PHP tags, an echo, or an invisible UTF-8 byte order mark. Leave off the closing ?> in PHP-only files. Output buffering can hide the problem, but fix the stray output first.

1. Read the warning: it tells you where to look

The session warnings look like these in PHP 8:

text
Warning: session_start(): Session cannot be started after headers have already been sent in /home/user/public_html/login.php on line 12

Warning: Cannot modify header information - headers already sent by (output started at /home/user/public_html/config.php:1) in /home/user/public_html/login.php on line 20

There are two locations, and they mean different things:

Part of the messageWhat it isWhat to do
"in login.php on line 20"The header call that failed: session_start(), header() or setcookie()Usually nothing is wrong here
"output started at config.php:1"The place where output first went to the browserGo here and remove the output

If the message only names one file, look at the lines above the failing call in that file, and at every file it includes before that point.

2. Why the error happens

An HTTP response is headers first, then the page. The moment PHP prints a single character, even a space, it sends the headers and starts on the page. After that, nothing can add a cookie or a redirect. session_start() needs to send the session cookie, header('Location: …') sends a redirect, and setcookie() sends a cookie, so all three fail once output has begun.

3. The usual causes and fixes

CauseWhat it looks likeFix
HTML before the PHP codeThe page starts with DOCTYPE or a blank line, then the opening PHP tagMove the PHP block with session_start() to the very top
An echo or print before the headerDebug output, a welcome message or var_dump() above session_start()Move the header call first, or remove the output
Whitespace after a closing tagA blank line after ?> at the end of an included fileDelete the closing ?> in PHP-only files
A byte order mark (BOM)"output started at …:1" and nothing visible on line 1Re-save the file as UTF-8 without BOM
A PHP notice or warning printed on screenAn earlier warning counts as outputFix that warning, and log errors instead of displaying them

The correct shape of a page that uses sessions:

php
<?php
session_start();

if (!isset($_SESSION['user_id'])) {
    header('Location: /login.php');
    exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<body>
  <p>Welcome back.</p>
</body>
</html>

The session and any redirect come first; HTML follows. Always call exit after a Location header so the rest of the script does not run.

4. The invisible culprit: a byte order mark

Some editors, notably older Windows ones, save UTF-8 files with three invisible bytes at the start, called a byte order mark. PHP prints them before your opening tag, so the warning points at line 1 of a file that looks perfectly clean.

To fix it, open the file in an editor such as VS Code or Notepad++, change the encoding to UTF-8 (not "UTF-8 with BOM") and save. From a terminal, you can find affected files with:

bash
grep -rlI $'^\xEF\xBB\xBF' --include='*.php' .

5. Drop the closing tag in PHP-only files

A file that contains only PHP, such as config.php or a class file, does not need the closing ?>. Leaving it off means no stray newline after it can ever be sent. The PSR-12 coding standard requires this, and most frameworks follow it.

php
<?php
// config.php: no closing tag at the end of this file
const DB_HOST = 'localhost';

6. Output buffering: a safety net, not a fix

Output buffering collects output in memory instead of sending it at once, so headers can still be sent later:

php
<?php
ob_start();
echo 'This is held in the buffer';
session_start();   // works, because nothing has been sent yet
ob_end_flush();

It is useful for templates that genuinely need to send a header late, but using it to silence the warning hides the real problem, which often returns when a file is included in a different order. Find and remove the stray output first. Buffering can also be turned on for a whole site with output_buffering in a .user.ini file.

7. See the errors without showing them to visitors

While you debug on a development copy, you can show errors on screen:

php
<?php
ini_set('display_errors', '1');
error_reporting(E_ALL);

Do not leave this on a live site. Displayed errors reveal file paths and details to visitors, and the error text itself is output, which causes more "headers already sent" warnings. On a live site, keep display_errors off and read the error log instead.

8. Other session errors you may see

  • "Session ini settings cannot be changed when a session is active": you called ini_set('session.…') after session_start(). Move the setting above it.
  • "Session cannot be started after headers have already been sent": the same stray-output problem as above.
  • "Ignoring session_start() because a session is already active": session_start() is called twice, often in an included file. Guard it with if (session_status() === PHP_SESSION_NONE) { session_start(); }.
  • Session data lost between pages: usually a cookie problem, such as switching between www and non-www, or a Secure cookie on an http:// page. See How to use PHP sessions in cPanel.

For session security and design, see Master PHP sessions with ease.

9. On Domain India hosting

On our cPanel servers, measured on 23 September 2026, PHP 8.3 ships with output_buffering off and display_errors off, and it logs errors to a file named error_log, which usually appears in the folder of the script that raised the error. So a stray space shows up as a warning in that log rather than on the page. Reviewing error logs in cPanel and DirectAdmin shows where to find them, and How to enable the display_errors option covers switching display on for testing. Change PHP settings with a .user.ini file or cPanel's MultiPHP INI Editor; a php_value line in .htaccess causes a 500 error on our servers.

cPanel Starter
₹125/mo + GST
  • 25 GB NVMe SSD Storage
  • 50 GB Monthly Bandwidth
  • 1 Website
  • 10 Email Accounts
See plan details

The card shows the live price, excluding 18% GST.

Frequently asked questions

What does "headers already sent" mean in PHP?

Your script printed some output, even a single space, before calling a function that sends an HTTP header, such as session_start(), header() or setcookie(). Once output starts, headers can no longer be sent. The warning's "output started at" part shows the file and line where the output began.

How do I fix "session_start(): Session cannot be started after headers have already been sent"?

Move session_start() to the very top of the script, before any HTML, blank lines or echo statements, and check every file included before it for stray output. Remove the closing ?> from PHP-only files and re-save files as UTF-8 without a byte order mark.

The warning points at line 1 but nothing is there. Why?

The file almost certainly starts with an invisible UTF-8 byte order mark, or has a space or blank line before the opening PHP tag. Re-save it as UTF-8 without BOM in your editor and remove anything before the opening tag.

Should I use ob_start() to fix header errors?

Only as a last resort. Output buffering hides the warning by holding output back, but the stray output is still there and the problem can return when files are included in another order. Find and remove the output first.

Should display_errors be on for my live website?

No. Keep it off on a live site and read the error log instead. Displayed errors reveal file paths to visitors and count as output, which can cause more headers already sent warnings.

Ready to track down the cause? Open your hosting services to reach cPanel and its error logs, and read Master PHP sessions with ease once the warning is gone.

Still seeing header errors?

Send us the page address and the full warning, and we will check the PHP settings and error log for your account with you.

Open a support ticket

Was this article helpful?

Your answer helps us decide what to improve next.

Still need help? Open a support ticket and our team will reply.

Prefer an app? Add this site to your home screen.Get the app
Fix PHP "Headers Already Sent" and Session Errors