You try to log in to WordPress, enter your credentials correctly, and see this message: “Cookies are blocked due to unexpected output. For help, please see this documentation or try the support forums.” The login fails. You re-enter your password, same result. The message points you to documentation but does not tell you what the unexpected output actually is.
This error means that something sent output to the browser before WordPress had a chance to set its authentication cookies. HTTP cookies are transmitted via response headers, and headers must be sent before any body content. If even a single character of output is sent before the headers, PHP cannot set cookies anymore. WordPress detects this and shows the error message.
The “unexpected output” is almost always invisible – a space, a newline, a byte order mark (BOM), or a PHP warning that was printed before the cookie headers. Finding and removing that output fixes the error.
How cookies and headers work#
When a browser requests a page, the server sends a response that consists of headers followed by a body, separated by a blank line:
HTTP/1.1 200 OK
Set-Cookie: wordpress_logged_in_abc=value; path=/; HttpOnly
Content-Type: text/html; charset=UTF-8
<html>...
The
Set-Cookie
header tells the browser to store a cookie. This header must be in the headers section – before the blank line that separates headers from body. Once PHP starts sending body content (even a single space or newline character), the headers are finalized and no more can be added.
WordPress sets authentication cookies during the login process using PHP’s
setcookie()
function. If any output was already sent – a stray character in a PHP file, a PHP warning, or whitespace before the opening
<?php
tag – PHP has already flushed the headers, and
setcookie()
fails. WordPress catches this failure and displays the “cookies are blocked” message.
The PHP warning that underlies this error is:
Warning: Cannot modify header information - headers already sent by (output started at /path/to/file.php:1)
The “(output started at /path/to/file.php:1)” part tells you exactly which file and line produced the premature output. This is the key to fixing the problem.
Cause 1: Whitespace before opening PHP tag#
The most common cause. A PHP file has a space, newline, or other character before the
<?php
opening tag. When PHP encounters this character, it sends it to the browser as output, which finalizes the headers.
This happens most often in
wp-config.php
and
functions.php
because these are the files that developers and hosting tools edit most frequently. A text editor that adds a newline at the beginning of the file, or a copy-paste operation that introduces invisible whitespace, is enough to trigger the error.
How to confirm
Enable debugging to see where output started:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
Check
wp-content/debug.log
for the “Cannot modify header information” warning. The “output started at” path tells you the exact file and line number.
If the line number is 1, the problem is characters before the
<?php
tag. If the line number is the last line, the problem is characters after the
?>
closing tag.
How to fix
Open the file mentioned in the error via SFTP or SSH and remove any characters before
<?php
. The opening tag must be the very first thing in the file – no spaces, no blank lines, no invisible characters.
# Check for BOM or whitespace before <?php
hexdump -C wp-config.php | head -3
The first bytes should be
3c 3f 70 68 70
(which is
<?php
in hex). If you see
ef bb bf
before that, the file has a UTF-8 BOM (byte order mark) that needs to be removed. If you see
20
(space) or
0a
(newline), there is whitespace before the tag.
To fix a BOM, open the file in a text editor that supports encoding selection and save it as “UTF-8 without BOM.” In VS Code, click the encoding indicator in the bottom bar, choose “Save with Encoding,” and select “UTF-8.”
Which files to check
The error message tells you which file caused the output. If it does not, check these files in order (they are loaded earliest in WordPress’s boot sequence):
-
wp-config.php– the most common culprit -
wp-content/themes/your-theme/functions.php - Any must-use plugins in
wp-content/mu-plugins/ - Any recently edited plugin file
Cause 2: Whitespace after closing PHP tag#
PHP files can optionally end with a
?>
closing tag. If there are any characters (including a newline) after the closing tag, those characters are sent as output. This is why the WordPress coding standards require omitting the closing
?>
tag in PHP files – it prevents exactly this problem.
How to confirm
The “output started at” line number is the last line of the file, and that line is
?>
followed by whitespace or a blank line.
How to fix
Remove the
?>
closing tag and any content after it. The file should end with PHP code and nothing else. This is the recommended practice for all PHP files in WordPress.
Cause 3: PHP warning or notice before headers#
A PHP warning or deprecation notice output before WordPress sets cookies causes the same problem. The warning text is the “unexpected output.” Common warnings that trigger this:
Deprecated: Function get_magic_quotes_gpc() is deprecated in /path/to/old-plugin/file.php
Warning: Undefined variable $foo in /path/to/plugin/settings.php
Notice: Trying to access array offset on value of type null in /path/to/theme/functions.php
How to confirm
Enable
WP_DEBUG_LOG
and disable
WP_DEBUG_DISPLAY
:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
The critical setting is
WP_DEBUG_DISPLAY
set to
false
. When this is
true
(or when
display_errors
is
On
in PHP), PHP warnings are printed to the browser, which counts as output and prevents cookies from being set.
Check
wp-content/debug.log
for warnings. The warnings themselves are not the problem – the problem is that they are being displayed to the browser instead of only logged.
How to fix
Suppress display while keeping logging. The
WP_DEBUG_DISPLAY = false
setting writes warnings to the log file without sending them to the browser. This fixes the cookie issue immediately while you work on resolving the underlying warnings.
Fix the warnings. PHP warnings indicate real issues – deprecated functions, undefined variables, incompatible code. Update the plugin or theme that generates the warnings. For deprecated function warnings, the plugin likely needs an update to be compatible with your PHP version. See How to display PHP errors and enable error reporting for a complete guide to PHP error configuration.
Check for plugins that force error display. Some debugging or development plugins call
ini_set('display_errors', 1)
or
error_reporting(E_ALL)
regardless of your wp-config.php settings. If you have debugging or development plugins active on a production site, deactivate them.
Cause 4: Plugin or theme outputting content too early#
Some plugins or themes output HTML, JavaScript, or other content during WordPress’s initialization phase, before cookies are set. This can happen when a plugin echoes output in a function hooked to an early action (like
init
or
plugins_loaded
) instead of waiting for the appropriate output hook (like
wp_head
or
wp_footer
).
How to confirm
The “output started at” path points to a specific plugin or theme file, and the line number is not the first or last line (ruling out whitespace/BOM issues). The line in question contains an
echo
,
print
, or HTML output statement.
How to fix
This is a bug in the plugin or theme. The developer needs to move the output to an appropriate hook. Check for plugin updates – the bug may be fixed in a newer version.
As a temporary workaround, you can use output buffering to capture premature output. Add this to the top of
wp-config.php
(immediately after
<?php
):
ob_start();
This starts PHP’s output buffer, which captures all output until the buffer is flushed. WordPress can then set cookies before the buffered output is sent. This is a workaround, not a fix – the plugin should be updated.
Cause 5: Extra PHP closing and opening tags#
If a file has multiple PHP blocks with whitespace between them, the whitespace between the blocks is sent as output:
<?php
// some code
?>
<?php
// more code
?>
The blank line between
?>
and
<?php
is HTML output (a newline character). This is technically valid PHP but causes header issues when it occurs in files loaded before cookie setting.
How to fix
Remove the intermediate
?>
and
<?php
tags. Use a single PHP block:
<?php
// some code
// more code
Cause 6: UTF-8 BOM in included files#
The byte order mark problem is not limited to wp-config.php. If any file loaded during WordPress’s early initialization has a BOM, the BOM bytes are sent as output. This includes:
-
wp-config.php -
wp-settings.php(if modified) - Any must-use plugin (
wp-content/mu-plugins/*.php) -
functions.phpin the active theme - Any file required by the above files
How to confirm
Use a hex editor or command-line tool to check for BOM:
file wp-config.php
If the output says “UTF-8 Unicode (with BOM) text”, the file has a BOM.
To check multiple files at once:
grep -rl $'\xEF\xBB\xBF' wp-content/themes/your-theme/
grep -rl $'\xEF\xBB\xBF' wp-content/mu-plugins/
How to fix
Remove the BOM from each affected file. Using
sed
:
sed -i '1s/^\xEF\xBB\xBF//' filename.php
Or open the file in an editor that shows encoding and save as UTF-8 without BOM.
Quick diagnostic process#
- Enable logging, disable display:
define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); - Try logging in again. The attempt will still fail, but the log now captures the details.
- Check the debug log:
tail -20 wp-content/debug.log - Look for “Cannot modify header information” – the “output started at” tells you the file and line.
- Open that file and fix the issue at the reported line (BOM, whitespace, PHP warning, premature echo).
- Clear browser cookies for the domain and try logging in again.
Most cases are resolved in under 5 minutes once you find the “output started at” location. The debug log makes this trivial.
How Hostney handles this#
WP_DEBUG_DISPLAY off by default. On Hostney,
display_errors
is set to
Off
at the PHP-FPM pool level. PHP warnings are logged but never sent to the browser. This prevents PHP warnings from interfering with cookie headers, even if a plugin generates deprecation notices or undefined variable warnings.
Per-site error logs. Each site has its own PHP error log at
~/.logs/{your-domain}-php.error.log
, making it easy to find the “output started at” information without searching through a shared server log.
Container isolation. Each site’s PHP configuration is independent. One site’s
display_errors
setting does not affect another site’s cookie behavior.
Summary#
“Cookies are blocked due to unexpected output” means something sent output to the browser before WordPress could set its authentication cookies via HTTP headers. The output is almost always invisible whitespace or a BOM before the
<?php
tag in wp-config.php, or a PHP warning being displayed instead of logged.
Enable
WP_DEBUG_LOG
with
WP_DEBUG_DISPLAY
set to
false
, check the debug log for “Cannot modify header information – headers already sent by”, and fix the file and line number it points to. The fix is almost always removing whitespace, removing a BOM, or suppressing error display. For a complete reference of WordPress errors and their quick fixes, see How to fix the most common WordPress errors.