Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

PHP has already started sending the response, but session_start() still needs to send session headers. Move session initialization to the request’s entry point, before HTML, whitespace, debugging output, included templates, cookies, or redirects. Then inspect the location named after output started at—that is usually where the real problem begins.

What the warning means

A PHP response has two parts:

  • HTTP headers: cookies, redirects, cache rules, content type, and status codes.
  • Response body: HTML, text, warnings, whitespace, and debugging output.

Once PHP sends body output, it may no longer be able to add or change HTTP headers. session_start() commonly sends a session cookie and other session-related headers, so it must run before any browser output. See the PHP documentation for session_start() and the documentation for header().

The HTML <head> element is unrelated to HTTP headers. A file named head.html.php can still emit response content and cause the warning.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How to read the error

Warning: session_start(): Cannot send session cookie - headers already sent by (output started at /path/index.php:1) in /path/includes/access.inc.php on line 42

There are two important locations:

  • index.php:1 is where PHP believes output first began. Inspect this location first.
  • access.inc.php:42 is where the later session_start() call failed. It is usually the symptom, not the original mistake.

Older PHP versions used wording such as session_start() [function.session-start]. That syntax is historical; the underlying problem is the same.

The fastest correct fix

Start the session before loading anything that might render output:

<?php

session_start();

require_once __DIR__ . '/includes/initialize.php';
require_once __DIR__ . '/includes/access.inc.php';

// Process POST requests, authentication, cookies, and redirects here.
// Render HTML only after that work is complete.

A common failing arrangement looks like this:

<?php

require 'includes/head.html.php';    // Emits HTML
require 'includes/access.inc.php';   // Calls session_start()

Use this order instead:

<?php

session_start();
require 'includes/access.inc.php';
require 'includes/head.html.php';

session_start() does not have to be literally the first byte of the file—comments and PHP declarations may precede it—but no response output may occur first.

Find the first output

Inspect the file and line shown after output started at, then check every file included before the failing call. Look for:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • echo, print, print_r(), or var_dump()
  • raw HTML before the PHP call
  • included templates that render markup
  • PHP warnings, notices, or deprecation messages
  • accidental whitespace or invisible characters
  • auto-prepended files or framework bootstrap code

You can ask PHP to report the origin programmatically:

<?php

$file = null;
$line = null;

if (headers_sent($file, $line)) {
    error_log("Headers already sent in {$file}:{$line}");
}

session_start();

headers_sent() can provide the file and line where output began. A temporary die() can help during local debugging, but never expose server filesystem paths to visitors in production.

Hidden output: whitespace and UTF-8 BOMs

Visible HTML is not required to trigger the warning. Check for:

  • a blank line or spaces before <?php
  • whitespace after a closing ?> tag
  • a UTF-8 byte-order mark (BOM) at the beginning of a file
  • a stray character in an included file
  • debug output left in a shared bootstrap file
  • an error message displayed before session initialization

PHP-only files should normally omit the closing tag:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php

function userIsLoggedIn(): bool
{
    return false;
}

UTF-8 itself is not the problem. The issue is an emitted BOM. If the first output location seems mysterious, save the file as UTF-8 without BOM and inspect its raw bytes. On systems with xxd available:

xxd -g 1 -l 16 path/to/file.php

The UTF-8 BOM sequence is:

ef bb bf

You can also search a project for likely causes:

grep -RInE 'session_start|headers*(|setcookies*(|echos|print_rs*(|var_dumps*(' .
grep -RInE '?>' --include='*.php' .

Separate session setup, request handling, and templates

The historical SitePoint example started sessions inside an authentication function after page markup had already been included. Moving the call into a function does not help if that function runs too late.

A clearer structure is:

  1. Bootstrap: start the session and load configuration.
  2. Controller: process forms, authentication, cookies, and redirects.
  3. Template: render HTML after request processing finishes.

For example:

<?php

session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $action = $_POST['action'] ?? '';

    if ($action === 'login') {
        // Validate credentials with password_verify().
        // After successful authentication:
        session_regenerate_id(true);
        $_SESSION['user_id'] = $userId;

        header('Location: dashboard.php');
        exit;
    }

    if ($action === 'logout') {
        $_SESSION = [];

        if (ini_get('session.use_cookies')) {
            $params = session_get_cookie_params();
            setcookie(
                session_name(),
                '',
                time() - 42000,
                $params['path'],
                $params['domain'],
                $params['secure'],
                $params['httponly']
            );
        }

        session_destroy();
        header('Location: login.php');
        exit;
    }
}

require __DIR__ . '/templates/page.php';

Use session_regenerate_id(true) after successful login to reduce session-fixation risk. Store a user ID and minimal authorization state in the session—not a plaintext password or a reusable password-derived value. Passwords should be checked with password_hash() and password_verify().

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Prevent repeated session initialization

Centralizing session startup is preferable. If a shared bootstrap can be loaded by multiple entry points, guard it when necessary:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php

if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}

This prevents redundant initialization but does not repair output that has already occurred. The bootstrap must still run before rendering. See PHP’s session_status() documentation.

Should you use ob_start()?

Output buffering delays body output:

<?php

ob_start();
session_start();

echo 'Page content';

ob_end_flush();

That can be legitimate when an application intentionally buffers a complete response, captures template fragments, or applies a deliberate output transformation. PHP documents this behavior in its output-control documentation.

However, adding ob_start() globally just to silence the warning is usually a poor permanent fix. It can hide incorrect execution order, increase memory use, alter error visibility, and make redirects or output handlers harder to reason about. Remove the accidental output and move session, cookie, and redirect logic earlier whenever possible.

Why it works locally but fails after deployment

Different environments may use different settings for output buffering, error display, automatic session startup, encoding, included files, or session storage. Relevant settings include:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • session.auto_start
  • session.use_cookies and session.use_only_cookies
  • session.cookie_secure, session.cookie_httponly, and session.cookie_samesite
  • session.save_path

Review the PHP session configuration documentation. A development warning displayed in the response can itself become the first output, so fix the underlying warning and configure production systems to log errors rather than display them to visitors. Do not use @session_start() to suppress the problem.

Final troubleshooting checklist

  1. Read the entire warning, especially output started at FILE:LINE.
  2. Inspect that file, including bytes before <?php.
  3. Check all parent files and includes executed earlier.
  4. Move session_start() to the request entry point.
  5. Remove HTML, debugging output, warnings, whitespace, and BOMs before it.
  6. Ensure setcookie() and header() also run before output.
  7. Use headers_sent($file, $line) when the source is unclear.
  8. Use a session_status() guard only to avoid duplicate startup.
  9. Use output buffering only as an intentional response-management technique.
  10. Test a new session, login, invalid credentials, refresh, logout, redirects, and deployment settings.

For web requests, the durable solution is nearly always the same: initialize the session and complete request processing first, then render the response.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.