Skip to main content
Ahosting Logo
  • Hosting
    • WordPress Hosting
      Fast, secure hosting for WordPress sites
    • Web Hosting
      Reliable, affordable hosting for sites
    • FFMpeg Hosting
      Fast hosting for FFmpeg projects
    • Reseller Hosting
      Start hosting biz with white-label plans
    • VPS Hosting
      Scalable VPS with full control & power
    • Dedicated Server
      High-power servers for max security
    • WooCommerce Hosting
      Fast hosting for WooCommerce shops
  • Domain
    • Register a Domain
      Secure your domain name in minutes
    • Domain Transfer
      Move domains to Ahosting with ease
    • Premium SSL Certificate
      Enterprise SSL to build customer trust
  • Support
    • Submit A Ticket
      Expert 24/7 help from our support team
    • Abuse Report
      Report abuse to keep network safe
    • Knowledge Base
      Quick answers via step-by-step guides
  • Company
    • Blog
      Expert articles to power your online growth
    • Compare Hosts
      Side-by-side comparison
    • Datacenter
      Secure, high tech datacenter for hosting
    • About Us
      Learn about our mission, values & team
    • Contact Us
      Contact sales for plans, pricing & advice
    • Sitemap
      Find info fast with our clear site map
My Account
Ahosting Logo
  • Hosting
    • Web Hosting
    • WordPress Hosting
    • FFMpeg Hosting
    • Reseller Hosting
    • VPS Hosting
    • Dedicated Server
    • WooCommerce Hosting
  • Domain
    • Register a Domain
    • Domain Transfer
    • Premium SSL Certificate
  • Support
    • Knowledge Base
    • Abuse Report
    • Submit A Ticket
  • Company
    • About Us
    • Contact Us
    • Blog
    • Sitemap
    • Datacenter
  • Legal
    • Privacy Policy
    • Terms of Service
    • Acceptable Use Policy
    • Service Level Agreement
    • Resource Abuse Policy
My Account

AHosting Blog

Headers Already Sent in WordPress: Find the Byte | AHosting

AHosting card on headers already sent: one stray byte locks the headers, and the line number shows which cause it is.

Matt Chrust

Director of Business Development, AHosting Matt has led business development at AHosting since the company’s founding in 2002. He writes about WordPress hosting infrastructure, server performance, and the evolving requirements of WordPress sites at scale.

Last Updated

October 21, 2026
Home » WordPress » Headers Already Sent in WordPress: Find the Byte | AHosting
  • Headers Already Sent in WordPress: What PHP Is Refusing to Do
    • Headers Already Sent: The Second Path Names the Victim
  • How to Read Output Started At When Headers Already Sent Appears
  • Headers Already Sent After a BOM or a Stray Space
    • Check the First Three Bytes for Headers Already Sent
  • Headers Already Sent Because of Text After the Closing Tag
    • Remove the Closing Tag to Stop Headers Already Sent
  • Headers Already Sent Because Something Printed a Notice
    • When the Named File Is a Core File
  • Paste Your Headers Already Sent Warning Into the Decoder
  • Why Headers Already Sent Works on One Host and Fails on Another
    • Headers Already Sent After You Move Hosts
  • What Headers Already Sent Breaks: Login, Redirects and the REST API
    • The Cookies Are Blocked Due to Unexpected Output Error
    • Headers Already Sent in the REST API Response
    • Why the Front Page Looks Fine
  • Find the Stray Byte That Causes Headers Already Sent Across a Site
    • A Scanner for BOMs and Text After the Closing Tag
    • A Fix Order for Headers Already Sent
  • What the Headers Already Sent Lab Did Not Cover
  • Frequently Asked Questions About Headers Already Sent in WordPress
    • What does the headers already sent warning mean in WordPress in 2026?
    • Why does headers already sent appear in wp-admin and login but not on the front page?
    • Headers already sent: BOM vs closing tag whitespace, which one does the line number reveal?
    • Output buffering vs fixing the file: which is the right fix for headers already sent in 2026?
    • How do I find which plugin causes headers already sent when the path names a core file?
    • Why can't I edit the header in WordPress when the headers already sent warning shows?
    • Which AHosting plans list SSH access or WP-CLI for headers already sent troubleshooting?
    • What should I send AHosting support about a headers already sent warning in 2026?
    • Can I hide the headers already sent warning with a .user.ini file on AHosting shared hosting?
    • Is headers already sent a security problem or only a coding mistake in 2026?
TL;DR

The headers already sent warning means PHP had already sent a byte of page output when WordPress tried to send a header or a cookie. Read the path after "output started at" first, because it names the file and line where that byte left. A line number of 1 points to a BOM, a line near the end points to text after a closing tag, and a core file points to a notice printed above it. In our lab a 3 byte BOM produced six warnings and no login.

Listen: the first path in the warning is the cause, and the line number says whether it is a BOM, a closing tag or a printed notice. By Matt Chrust, Director of Business Development, AHosting.

Headers Already Sent in WordPress: What PHP Is Refusing to Do

The headers already sent warning is PHP refusing to change an HTTP header after the page has started. A web response has two parts, the headers first and the body second. Once a single byte of body leaves PHP, the headers are locked, and a later call to header() or setcookie() fails. The PHP manual for header() says why: output can come from “blank lines in a file”, not only from HTML.

Here is the full message, with a made-up path:

Warning: Cannot modify header information - headers already sent by (output started at /home/user/public_html/wp-config.php:1) in /home/user/public_html/wp-login.php on line 525
How one stray byte causes the headers already sent warning Step 1: PHP starts the response. Step 2: a stray byte such as a BOM, a space or a printed notice leaves PHP; the output started at path names this step. Step 3: the HTTP headers are now locked. Step 4: setcookie or header runs too late and prints the warning; the in file on line path names this step. 1 PHP starts the response 2 A stray byte leaves BOM, space, notice 3 The headers are now locked 4 setcookie or header runs too late: Warning Read this first "output started at" path Only the victim "in file on line" path Lab: WordPress 7.1.3, PHP 8.5.10 built-in server, one workstation

Headers Already Sent: The Second Path Names the Victim

The message has two halves, and only one of them helps. The first half, output started at, names the file and line where the first byte left PHP. That is the cause. The second half, in ... on line, names the code that tried to send a header and was too late. Core files appear there constantly, and editing them is the wrong move.

To check this, I installed WordPress 7.1.3 on a local PHP 8.5.10 server and caused each problem on purpose. Every row of the tables below comes from one of those runs. The setup is a single workstation, not an AHosting server, and the last section lists what it did not cover.

How to Read Output Started At When Headers Already Sent Appears

Start with the file and the line number. Together they sort the problem into one of five cases, and each case has a different fix.

What output started at namesMost likely causeWhat to checkReproduced in the lab
A plugin, theme or wp-config.php file, line 1A BOM, or a space or line break before the PHP opening tagThe first three bytes of that fileYes. A BOM in wp-config.php gave six warnings and no login cookie
A plugin or theme file, a line near the endText after the closing tag: a second newline or a spaceThe last lines of that fileYes. A closing tag plus two newlines was reported at line 5
A plugin or theme file, a line in the middleThe code printed something: an echo, or a notice PHP displayedThe named line, then any message printed above the warningYes. A notice raised on line 2 was reported at line 2
A core file such as wp-includes/functions.phpA Notice or Deprecated message printed by core for another pluginThe first message above the first warning; it names the real fileYes. A deprecated call was reported at functions.php line 6260
Nothing is shown at alldisplay_errors is off, so the warning is hidden but the problem remainsThe PHP error log, and the login page error textYes. Login returned 200 with no cookie and an unexpected output error
Headers Already Sent Decoder Table: five readings of the output started at path, each tied to a run on WordPress 7.1.3.

Headers Already Sent After a BOM or a Stray Space

A byte order mark, or BOM, is the character U+FEFF placed at the start of a file. In UTF-8 it is the three bytes EF BB BF, and the W3C article on the byte-order mark warns that a BOM at the start of a PHP file begins output before a header call runs. Some editors add it on save, and none shows it. PHP treats any bytes before the opening tag as output. The PSR-1 standard therefore requires PHP code to use UTF-8 without BOM.

In the lab I put those three bytes before the opening tag of wp-config.php and submitted the login form with the right password. WordPress answered 200 and set no login cookie. It printed six warnings, all saying output started at wp-config.php:1. One invisible mark produced six messages, because each header and cookie call in the login request failed on its own.

Check the First Three Bytes for Headers Already Sent

A line number of 1 is the tell, but check before you edit. In a shell, head -c 3 wp-config.php | od -An -tx1 prints ef bb bf when the BOM is there. I ran that on macOS only. A space or tab before the opening tag shows up as 20 or 09 in the same output.

To fix it, save the file again as UTF-8 without BOM. In VS Code, the encoding button in the status bar lets you save the file with a different encoding. Then request the page again and confirm that the first byte is the opening tag.

Headers Already Sent Because of Text After the Closing Tag

The closing tag is the second classic source. PHP swallows one newline that comes right after the closing tag, and anything beyond that newline is output. The lab shows the edge exactly: a must-use plugin ending in a closing tag plus one newline worked, and the same file with two newlines broke login.

In that run the report said output started at the plugin file, line 5, although the file had four lines. Notably, the second newline sits on a fifth line that looks empty in an editor, which is why this one is hard to see.

Remove the Closing Tag to Stop Headers Already Sent

Because the closing tag at the end of a file is optional, leaving it off removes this whole class of error. The WordPress PHP coding standards say that omitting the closing tag at the end of a file is preferred. For a file you own, such as a custom plugin or a functions.php, delete the final closing tag and any lines below it.

Do not edit a core file or a vendor plugin this way. Replace it with a clean copy from its author instead, because your change disappears at the next update anyway.

Headers Already Sent Because Something Printed a Notice

Whitespace is only one kind of output. A PHP notice, warning or deprecation message is output as well, and WordPress shows them in the page when debugging is on. The WordPress debugging handbook says the WP_DEBUG_DISPLAY default is true, which “shows errors and warnings as they are generated”. A site that shows one notice then shows a headers already sent warning right behind it.

In the lab, a must-use plugin raised a notice on init with debugging on. Login returned 200 with no cookie, and the warning said output started at that plugin, line 2, the very line that raised the notice. So far the file named is the culprit.

When the Named File Is a Core File

The next run is the one that misleads people. A plugin called a deprecated function on init. WordPress printed the Deprecated message itself, so the warning said output started at wp-includes/functions.php:6260. Core is named, and core is innocent. The real file is in the message printed above the first warning.

Read that first message, then rename the plugin folder it names and test again. To stop the display while you fix the cause, set WP_DEBUG_DISPLAY to false and WP_DEBUG_LOG to true. If a debug log stays on, read what a public debug log exposes so that you do not trade one problem for another.

Paste Your Headers Already Sent Warning Into the Decoder

The decoder below applies the table to your own warning. It reads the line number and the path, then names the likely cause and the section to read next. It reports what the table says and nothing more, so a plugin with unusual output still needs a person to read the file.

Warning Line Decoder

Paste the whole warning. The text stays in your browser and is not sent anywhere.

Open the matching section

Why Headers Already Sent Works on One Host and Fails on Another

The same files can pass on one server and fail on another. The reason is output_buffering. The PHP output control settings list its default as “0” and give 4096 as an example value. With a buffer, PHP holds early output in memory, so headers can still go out first.

I ran the same broken files with and without a 4096 byte buffer. The result is Table 2.

Stray outputoutput_bufferingLogin result
3 byte BOM in wp-config.php0200, no login cookie, six warnings
3 byte BOM in wp-config.php4096302 redirect, login cookie set, no warning
20 byte echo from a plugin4096302 redirect, login cookie set
5000 byte echo from a plugin4096200, no login cookie, warning at the echo line
3 byte BOM, display_errors off0200, no login cookie, no warning shown
Same Files, Different PHP Settings: how output_buffering changes the login result in the lab.

Headers Already Sent After You Move Hosts

Because the setting belongs to the server, a migration can start this warning without anyone touching a file. A BOM that a lenient buffer hid for years surfaces the day the new server has none. If the warning appears right after a move, the migration walkthrough covers the checks around it, and the stray byte is still the thing to find.

Raising the buffer would hide the symptom in the lab, and I would not ship it. The 5000 byte row shows the hiding stops working as soon as the output grows. I did not read this setting on any AHosting server, so this post makes no claim about its value there. Ask your host, or look at it in phpinfo().

What Headers Already Sent Breaks: Login, Redirects and the REST API

The warning is the visible part. The damage is what failed to send. In login, wp_set_auth_cookie() makes three setcookie() calls at pluggable.php:1193 to 1195, and all three failed in the lab. Redirects use header( "Location: ..." ) at pluggable.php:1542, and the admin sends a Referrer-Policy header at functions.php:7310. None of these has a guard.

The Cookies Are Blocked Due to Unexpected Output Error

Hiding the warning does not hide the failure. With display_errors off, the lab login page came back with “Cookies are blocked due to unexpected output.” These lines of wp-login.php print it when the login cookie is missing and headers_sent() is true. The error is the same problem seen from the login form.

Headers Already Sent in the REST API Response

REST responses need clean JSON. With a BOM in a must-use plugin, a REST request returned a body that began with the bytes ef bb bf, then warning markup, and a JSON parser rejected it. The editor shows that as a bad response, so the not a valid JSON response guide is the next place to look.

Why the Front Page Looks Fine

The front end is quieter. WordPress tests headers_sent() before it sends most front-end headers, at class-wp.php:573 to 581, and nocache_headers() returns early the same way. In the lab, the front page returned 200 and printed no warning, yet the stray byte still sat ahead of the doctype. A clean front page therefore does not clear a file.

Redirect and login problems from cookies deserve their own check, and the too many redirects guide covers the cookie and redirect side separately.

Find the Stray Byte That Causes Headers Already Sent Across a Site

Usually the warning names the file, and you are done. Sometimes you migrate a site or inherit one, and you want every file checked at once. On a shared web hosting plan that lists SSH access, a short PHP script can do it. I wrote one and tested it on seven sample files.

A Scanner for BOMs and Text After the Closing Tag

It reads every .php file under a folder and prints two kinds of finding. A first token made only of a BOM or whitespace is reported with its bytes, and so is whitespace after a final closing tag. It left alone a closing tag followed by one newline, a file with no closing tag, and a template with real markup.

<?php
// Usage: php find-stray-output.php /path/to/wp-content
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($argv[1], FilesystemIterator::SKIP_DOTS));
foreach ($it as $f) {
    if (substr($f, -4) !== '.php') { continue; }
    $t = token_get_all(file_get_contents($f));
    $first = $t[0];
    if (is_array($first) && $first[0] === T_INLINE_HTML && trim($first[1], "\xEF\xBB\xBF \t\r\n") === '') {
        echo $f . ':1 starts with ' . bin2hex(substr($first[1], 0, 6)) . "\n";
    }
    $last = end($t);
    $prev = prev($t);
    if (is_array($last) && $last[0] === T_INLINE_HTML && is_array($prev) && $prev[0] === T_CLOSE_TAG && trim($last[1]) === '') {
        echo $f . ' ends with ' . bin2hex(substr($last[1], 0, 6)) . " after the closing tag\n";
    }
}

The script is read-only. It cannot find an echo or a notice, so the decoder above still comes first. If you run many client sites, run it once per account, because a reseller hosting account keeps a separate cPanel account for each client.

A Fix Order for Headers Already Sent

  1. Read the first warning, and ignore the repeats. They share one cause.
  2. On line 1, check the first three bytes and save the file as UTF-8 without BOM.
  3. Near the end of a file, delete the closing tag and any lines under it.
  4. With a core file, read the message printed above the first warning and fix the file it names.
  5. Test login in a private window, because a cached admin session hides the cookie failure.

What the Headers Already Sent Lab Did Not Cover

The lab used WordPress 7.1.3 with SQLite instead of MySQL, PHP 8.5.10 and the built-in web server, on one workstation. I did not test LiteSpeed, LSAPI or php-fpm, multisite, a cPanel editor, or any browser. The scanner ran on seven sample files, not on a real site, and the shell commands ran on macOS only. Every number in this guide comes from that setup, so treat the line numbers as WordPress 7.1.3 line numbers.

Finally, if your warning fits none of the five rows, the file is not the problem and the next step is a person. If your site runs on our WordPress hosting plans, open a ticket with the full warning line, from the word Warning to the last line number, and we will look at it with you.

Frequently Asked Questions About Headers Already Sent in WordPress

What does the headers already sent warning mean in WordPress in 2026?

Specifically, PHP had already sent page output when WordPress tried to send a header or a cookie, so PHP refused. The path after output started at names the file and line where the first byte left, and that is the place to look. The second path in the message only names the code that was too late.

Why does headers already sent appear in wp-admin and login but not on the front page?

In practice, WordPress core tests headers_sent before it sends most front-end headers, so the front end stays quiet. Login, admin screens and redirects call setcookie or header without that test, so they print the warning. The stray output can still be on the front page, ahead of the doctype.

Headers already sent: BOM vs closing tag whitespace, which one does the line number reveal?

Specifically, a line number of 1 points to a BOM or to text before the PHP opening tag. A line number near the end of a file points to text after the closing tag. In our lab a closing tag followed by two newlines was reported at line 5 of a file with four lines.

Output buffering vs fixing the file: which is the right fix for headers already sent in 2026?

Specifically, fixing the file is the right fix. Output buffering only holds early output in memory, and it stops helping once the output outgrows the buffer. In our lab a 20 byte echo passed with a 4096 byte buffer, and a 5000 byte echo failed with the same setting.

How do I find which plugin causes headers already sent when the path names a core file?

Specifically, scroll up to the first message printed above the first warning, because a Notice or Deprecated line names the real file. In our lab a deprecated call from a must-use plugin was reported at wp-includes functions.php line 6260, while the message above it named the plugin. Then rename that plugin folder and test again.

Why can’t I edit the header in WordPress when the headers already sent warning shows?

Indeed, this warning is about HTTP headers, the lines a server sends before the page, and not the header area you edit in the Customizer or the Site Editor. No theme setting changes it. The fix is in a PHP file that printed something too early, so use the path after output started at.

Which AHosting plans list SSH access or WP-CLI for headers already sent troubleshooting?

Specifically, the AHosting WordPress Hosting page lists WP-CLI access and SSH access (jailed shell), and the Web Hosting page lists SSH access (jailed shell), as read on October 20, 2026. Neither page says which tools exist inside the shell, so check that PHP runs there before you rely on a scanner.

What should I send AHosting support about a headers already sent warning in 2026?

Specifically, send the full warning line, from the word Warning to the last line number, including both paths. The line holds file paths for your account and nothing secret. If a notice printed above it, add that line too, and say which file you already checked.

Can I hide the headers already sent warning with a .user.ini file on AHosting shared hosting?

However, hiding the message does not fix the cause. Our servers read .user.ini files, so display_errors can be set there, but in our lab login still failed with display_errors off and WordPress printed that cookies are blocked due to unexpected output. Fix the file that printed the byte.

Is headers already sent a security problem or only a coding mistake in 2026?

Typically it is an editor or coding mistake, and a BOM does not prove a hack. That said, if the output at the named line is text you did not write, treat the file as suspect and compare it with a clean copy from the plugin or theme author.

Related posts:

AHosting card on the REST API encountered an error, showing the three Site Health REST labels and what failed for each one.The REST API Encountered an Error? WordPress Never Reached It WooCommerce Black Friday hosting countdown showing checkout ceiling arithmetic across a twelve-week scheduleWooCommerce Black Friday Hosting: The 12-Week Countdown Site: AHosting.net  Hosting card on ERR_TOO_MANY_REDIRECTS: four loop signatures, what repeats in the redirect chain, and the likely cause of each.ERR_TOO_MANY_REDIRECTS in WordPress: Find the Looping Rule With curl Security WordPress InstallationThe All-Inclusive Guide To Securing Your WordPress Installation
«Sorry, You Are Not Allowed to Access This Page in WordPress: Find the Cause
PHP Critical Security Release: What WordPress Site Owners Should Do Now»

Categories

  • CMS
  • Concrete5
  • Drupal
  • FFmpeg / Video Hosting
  • Hosting Guides
  • How To
  • Joomla
  • Security
  • SEO
  • Uncategorized
  • Video Content
  • Web Hosting News
  • WooCommerce
  • WordPress

Lets Connect!

  • X
  • Facebook
  • LinkedIn
  • Instagram
  • YouTube
  • Pinterest
Ahosting Logo

Hosting

  • WordPress Hosting
  • Web Hosting
  • FFmpeg Hosting
  • WooCommerce Hosting
  • Reseller Hosting
  • VPS Hosting
  • Dedicated Server

Domain

  • Register a Domain
  • Domain Transfer
  • Premium SSL Certificate

Support

  • Knowledge Base
  • Abuse Report
  • Submit A Ticket

Company

  • Compare Hosts
  • About Us
  • Datacenter
  • Contact Us
  • Blog
  • Sitemap

Legal

  • Privacy Policy
  • Terms of Service
  • Acceptable Use Policy
  • Service Level Agreement
  • Resource Abuse Policy
  • Hosting
    • WordPress Hosting
    • Web Hosting
    • FFMpeg Hosting
    • WooCommerce Hosting
    • Reseller Hosting
    • VPS Hosting
    • Dedicated Server
  • Domain
    • Register a Domain
    • Domain Transfer
    • Premium SSL Certificate
  • Support
    • Knowledge Base
    • Abuse Report
    • Submit A Ticket
  • Company
    • About Us
    • Datacenter
    • Contact Us
    • Blog
    • Sitemap
  • Legal
    • Privacy Policy
    • Terms of Service
    • Acceptable Use Policy
    • Service Level Agreement
    • Resource Abuse Policy

Copyright © 2026 All Rights Reserved

Ahosting, Inc. BBB Accredited Business, A+ rating
Facebook X/Twitter Instagram LinkedIn YouTube
Limits printed per plan, not hidden WP Bronze: 30 entry processes, 2 GB memory, LiteSpeed + LSCache
$2.79/mo same price at renewal
Start for $2.79/mo 30-day money-back · free migration ★★★★★4.5 Trustpilot

WordPress 36-month term · $100.44 today

WP Bronze $2.79/mo WP Silver $3.79/mo WP Gold $4.79/mo Compare WordPress

WooCommerce

WooStart $3.79/mo WooPower $16.79/mo Compare WooCommerce

FFmpeg

FFStart $16.79/mo FFPower $28.79/mo Compare FFmpeg

Web hosting

Bronze $2.79/mo Silver $3.79/mo Gold $4.79/mo Compare Web hosting
Limits printed per plan, not hidden ★ 4.5 Trustpilot
Start for $2.79/mosame price at renewal · 30-day money-back