Skip to content

How to fix "Cannot modify header information - headers already sent" in WordPress

PHP was asked to send a header, such as a redirect or the login cookie, after part of the page had already gone out. The message names two files. The first, after "output started at", is where the stray output came from, and it is the one to fix.

By
WP Ministry
Published
Tested on
WordPress 7.1.3, PHP 8.3.35

In short

  • Nothing is lost. A redirect or a cookie could not be sent, so a login or a page that sends you on did not finish.
  • The message names two files. Fix the first, after "output started at". The second is only where PHP noticed.
  • The usual cause is a blank line or a space before the opening PHP tag or after a closing one, in wp-config.php or functions.php.
  • A PHP warning printed on the page is output too. Send errors to a log and the headers go out again.
  • A login screen that says "Cookies are blocked due to unexpected output" is the same fault with the warning hidden.
  • Some servers hold output back before sending it, which hides the mistake. It can first show after a move to a new host.

PHP sends a page in two parts: the headers first, then the page itself. Headers carry what a browser acts on before it shows anything, such as a redirect to another address or the cookie that keeps you logged in. PHP's manual says a header "must be called before any actual output is sent", and it counts a blank line in a file as output. This warning means WordPress asked for a header after something, often one blank line, had already gone out.

Nothing has been lost. What failed is the redirect or the cookie, so a login does not hold, or a page that should send you on stays where it is. Remove the stray output and both work again.

Read the message first

The message names two files, and only the first needs fixing.

text
Warning: Cannot modify header information - headers already sent by (output started at /home/example/public_html/wp-config.php:104) in /home/example/public_html/wp-includes/pluggable.php on line 1542
  • After "output started at" is the file that sent something too early, then a colon and the line it came from. Here that is line 104 of wp-config.php. This is the file to fix.
  • The second path is only where PHP noticed. wp-includes/pluggable.php holds the WordPress functions that send redirects and set the login cookies, which is why it is named so often. Leave it alone.
The first path isWhat it usually meansFix
wp-config.php or a theme's functions.php, on line 1 or at the very endA blank line or a space outside the PHP tags. If line 1 looks clean, a byte order mark.First, then fourth
The file and line of another warning or notice printed just above this oneThat message is the outputSecond
A file in a plugin's folderCode that prints as soon as it is loadedThird
A file inside wp-admin or wp-includesA core file that was changedFifth

A server that keeps PHP's errors off the page shows no warning. The login screen says "Error: Cookies are blocked due to unexpected output." in its place, or the page stays blank. How to turn on WordPress debug mode shows how to send errors to a log, where the same line is written. Paste it into the WordPress error message and debug.log decoder to see which plugin or theme it points at. It is read in your browser, and nothing is sent.

Where it goes wrong

A page request passes through each of these in turn. This one comes from PHP, the language WordPress runs on.

  1. Browser
  2. DNS
  3. HTTPS
  4. CDN or firewall
  5. Web server
  6. PHP (this error comes from here)
  7. WordPress
  8. Database and files

What causes it

  • A blank line or a space sits outside the PHP tags of a file

    Common

    Anything before the opening PHP tag or after a closing one is sent to the browser as part of the page. It is usually in wp-config.php or a theme's functions.php, and usually arrived with an edit.

    Fix: Remove what is outside the PHP tags

  • A PHP warning or notice is printed on the page

    Common

    When PHP's errors are shown on the page, each one is output. One raised early, by any plugin or theme, goes out before the headers do.

    Fix: Keep PHP's errors off the page

  • A plugin prints something as soon as it is loaded

    Sometimes

    Code that prints the moment its file is read, where it should wait until the page is being built, puts its text ahead of the headers.

    Fix: Switch off the plugin the first path names

  • The file was saved with a byte order mark

    Sometimes

    Some text editors put three bytes that do not show at the start of a file they save as UTF-8. PHP sends them like anything else in front of the opening tag.

    Fix: Remove a byte order mark from the file

  • One of WordPress's own files was changed

    Rare

    The first path is inside wp-admin or wp-includes. Those files are the same on every site running that version, so one that sends output early has been edited or damaged.

    Fix: Replace WordPress's own files with fresh copies

How to fix it

Remove what is outside the PHP tags

  • Easy
  • Back up first
  • About 10 minutes
  • Steps tested on WordPress 7.1.3
  1. Step 1: Open the file the first path names

    Use SFTP or your host's file manager, and a plain text editor, not a word processor.

  2. Step 2: At the top, make the opening tag the first thing in the file

    If the message gives line 1, delete every blank line and space in front of <?php.

  3. Step 3: At the end, delete the closing tag and everything after it

    If the message gives a line at the end of the file, the output comes after a closing ?> there. A file that ends in PHP does not need that tag, and with it gone nothing can follow it.

  4. Step 4: Save, upload and reload

    The warning is gone, and the login or the redirect works.

With SSH access to a Linux server, run these from the folder that holds wp-config.php, with the path of your file in place of wp-config.php. The first shows the top of the file with its hidden characters. Each $ marks the end of a line.

bash
head -n 3 wp-config.php | cat -A

A clean file starts <?php$. A $ alone is a blank line. Keep a copy of the file, then delete the blank lines above the first line that has anything on it, and the spaces in front of that line:

bash
cp wp-config.php ~/wp-config-before.php
sed -i '/[^[:space:]]/,$!d' wp-config.php
sed -i '1s/^[[:space:]]*//' wp-config.php

For the end of the file, print from a few lines above the line in the message:

bash
sed -n '100,$p' wp-config.php | cat -A

If the closing ?> is on line 103, delete from that line to the end, then have PHP check the file:

bash
cp wp-config.php ~/wp-config-before.php
sed -i '103,$d' wp-config.php
php -l wp-config.php

It answers "No syntax errors detected" when PHP can read the file.

To undo it: Upload the copy of the file you downloaded before you changed it.

Keep PHP's errors off the page

  • Easy
  • Back up first
  • About 10 minutes
  • Steps tested on WordPress 7.1.3

When the page shows another warning or notice above this one, and "output started at" names the file and line that message does, the message itself is the output. The fault it reports may be small. Printing it is what breaks the login.

  1. Step 1: Send errors to a log instead of the page

    In wp-config.php, replace the WP_DEBUG line with the three lines the debug mode guide gives, or run these. WP_DEBUG_DISPLAY set to false does nothing unless WP_DEBUG is true.

    bash
    wp config set WP_DEBUG true --raw
    wp config set WP_DEBUG_LOG true --raw
    wp config set WP_DEBUG_DISPLAY false --raw
  2. Step 2: Reload the page

    The headers are sent again. The message that was on the page is now in wp-content/debug.log.

  3. Step 3: Deal with what the log names

    The path in the line names the plugin or theme. Update it, or send the line to its author. The debug mode guide covers turning the log off again and deleting it.

To undo it: Set WP_DEBUG back to false and remove the other two lines.

Switch off the plugin the first path names

  • Easy
  • Low risk
  • About 10 minutes
  • Steps tested on WordPress 7.1.3

When the first path is in a plugin's folder, that plugin prints something the moment it is loaded. WordPress checks for this when a plugin is activated, and the Plugins screen then says the plugin generated "unexpected output during activation".

  1. Step 1: Deactivate the plugin

    The folder after wp-content/plugins/ in the path is the plugin's. With WP-CLI:

    Without it, use the Plugins screen. If you cannot log in, how to deactivate WordPress plugins when you are locked out of the dashboard gives the ways to do it from outside, such as renaming the plugin's folder over SFTP.

    bash
    wp plugin deactivate plugin-folder-name
  2. Step 2: Reload the page

    If the warning is gone, you have found the cause. Update the plugin, or tell its author what the message said.

If the first path is a line of functions.php where you pasted a snippet, take the snippet out: the first fix on the syntax error page shows how. Before you add it again, move whatever it prints into a function attached to a hook such as wp_head, so that it runs while the page is being built.

To undo it: Activate the plugin again.

Remove a byte order mark from the file

  • Takes care
  • Back up first
  • About 10 minutes
  • Steps tested on WordPress 7.1.3

A byte order mark is three bytes, EF BB BF, that some editors put at the start of a file they save as UTF-8. It is not a character you can see, so line 1 looks clean. PHP sends it to the browser like anything else in front of <?php.

  1. Step 1: Save the file again without the mark

    Open it in a code editor, save it with plain UTF-8 as the encoding and not one with BOM in its name, and upload it.

  2. Step 2: Or look for the mark over SSH

    Print the file's first three bytes:

    ef bb bf is a byte order mark. A clean file gives 3c 3f 70, the characters <?p.

    bash
    head -c 3 wp-content/themes/your-theme/functions.php | od -An -tx1
  3. Step 3: Strip it

    On a Linux server, this removes those three bytes from the start of line 1 and changes nothing else:

    Print the first three bytes again. They are now 3c 3f 70.

    bash
    sed -i '1s/^\xEF\xBB\xBF//' wp-content/themes/your-theme/functions.php

To undo it: Upload the copy of the file you downloaded before you changed it.

Replace WordPress's own files with fresh copies

  • Takes care
  • Back up first
  • About 20 minutes

When the first path is inside wp-admin or wp-includes, do not repair the file by hand. Replace it.

  1. Step 1: See which files differ from the originals

    It compares the installed files with WordPress.org's checksums for your version and lists each one that does not match.

    bash
    wp core verify-checksums
  2. Step 2: Download the same version again

    --force overwrites the files that are there. --skip-content leaves out the default themes and plugins. Add --locale= with your site's language code if WordPress is not installed in US English.

    bash
    wp core download --version=$(wp core version) --skip-content --force
  3. Step 3: Without WP-CLI, replace the one file

    Download your version of WordPress from WordPress.org, unzip it, and upload the file the message names over the one on the server.

When to get help

If the file the message names is clean at both ends, has no byte order mark, and the warning is still there, the output comes from code that runs inside that file, and finding it means reading the code. The same holds if the warning returns each time a plugin or theme updates.

Common questions

Why did this start after I moved to a new host?

The stray output was probably there all along. PHP can hold a page back in a buffer before sending it, a setting named output_buffering, and while the page is held back a header can still be added. The setting is off by default, and the settings file PHP ships for live servers sets a buffer of 4096 bytes. A blank line that did no harm with the buffer on gives this warning with it off. Fix the file, and the site works on both.

Is it safe to delete the closing tag at the end of a PHP file?

Yes, when the file ends in PHP, as wp-config.php and functions.php do. PHP's manual and WordPress's coding standards both prefer leaving it out. PHP also swallows one line break straight after a closing ?>, which is why a file can end with the tag for years and fail only when a second blank line is added.

The warning is gone and something is still wrong. What now?

If the site went down the moment you saved, with a line beginning "Parse error", the edit removed a character PHP needs: see the syntax error page linked above. If the login screen still comes back, with no message, see the login page that keeps refreshing. If pages are blank, see the white screen of death.

More on this subject

Would you rather we fixed it?

Quick Fix is $49. One issue, one site, up to about an hour. No fix, no fee. 30-day warranty. It starts with a free diagnosis.