The PHP warning Cannot modify header information - headers already sent by appears when your script tries to send an HTTP header after some output has already gone to the browser. HTTP headers must precede the body. Once PHP sends even one byte of body, it must flush the headers first, and after that header(), setcookie(), session_start() and redirects can no longer change them.
The message tells you exactly where to look:
Warning: Cannot modify header information - headers already sent by (output started at /var/www/app/config.php:3) in /var/www/app/login.php on line 27
The part in parentheses is where output began (config.php, line 3). The "in ... on line 27" part is the later line that tried to send a header. The bug is almost always at the first location, not the second. For the WordPress-specific version of this error see WordPress cannot modify header information.
What counts as output
Any of these, before your header call:
- Whitespace or a blank line before
<?phpor after a closing?> - A UTF-8 byte order mark (BOM) at the start of a file, saved by some editors
echo,print,var_dump,print_r, or inline HTML- A PHP notice, warning or deprecation message printed to the page
- An included file that outputs anything, including a trailing newline after
?> - Debug output left in a hook, class or template
Find the real cause
- Open the file and line named in "output started at".
- If the line is
1, suspect a BOM or whitespace before<?php. Check for a BOM:
head -c 3 config.php | xxd
efbbbf at the start means a BOM. Remove it with an editor option such as "Save with encoding: UTF-8 without BOM", or:
sed -i '1s/^\xEF\xBB\xBF//' config.php
- Scan a codebase for files that end with a closing tag followed by blank lines:
grep -rlP '\?>\s*\n+\z' --include=*.php .
The closing ?> tag is optional in files that contain only PHP, and omitting it removes the whole class of trailing-whitespace bugs. PSR-12 requires omitting it in such files.
- If the "output" is an error message, the real problem is the notice before it. Fix that, or raise the problem in logs instead of on screen (
display_errors = Off).
Fixes
Move header calls before any output
Run session_start(), setcookie(), header() and redirects at the top, before templates or echo. Separate logic from presentation: do the work and the redirect first, then include the view.
<?php
declare(strict_types=1);
session_start();
if (!isset($_SESSION['user'])) {
header('Location: /login');
exit;
}
Always call exit after a redirect header so the script does not keep running.
Use output buffering as a safety net
ob_start() at the top of the entry script holds output in memory, so headers can still be sent later:
<?php
ob_start();
Or set it in php.ini:
output_buffering = 4096
This is a mitigation, not a fix. It hides the symptom while the stray output remains, and large buffers use memory. Use it for legacy code you cannot restructure now.
Check included files
Search every file included before the header call, including config, helpers and language files. A single trailing newline in an included file is enough. Binary-safe diffs, or opening the file in an editor that shows invisible characters, help.
Watch for functions that send headers implicitly
session_start() sends cookie headers. header() with a redirect, http_response_code(), setcookie() and setrawcookie() do too. Check with headers_sent($file, $line):
if (headers_sent($file, $line)) {
error_log("Headers already sent in $file:$line");
}
Check the result from outside
After fixing, confirm redirects and cookies behave. The HTTP Header Checker shows the headers you now send, the रीडायरेक्ट चेकर follows the redirect chain, and the Cookie Checker confirms Set-Cookie flags. Use the HTTP स्टेटस चेकर to make sure the page now returns 302 or 200, not a 500. If a fatal error follows the warning, see fix 500 internal server error.
Prevent it
- Omit
?>at the end of pure PHP files. - Configure your editor for UTF-8 without BOM, LF endings, and a single final newline inside PHP code only.
- Add a linter step that detects output in config files.
- Keep
display_errorsoff in production. - Test login, logout and checkout flows after deploys, since these send headers and cookies. An अपटाइम मॉनिटर with a keyword check on the login page catches regressions.
अगले कदम
- Review PHP parse error: syntax error.
- Learn about cookie security in cookie flags Secure, HttpOnly and SameSite.
- Debug redirects with HTTP redirects 301 vs 302.
- Read WordPress white screen of death if a plugin is breaking pages.
सामान्य प्रश्न
Why does the error point to a different file than the one I edited?
The first location is where output began. The second is where you sent a header. Fix the first.
Is ob_start a good fix?
It is a workable stopgap. Finding and removing the stray output is the proper fix.
Why only on some servers?
Different display_errors and output_buffering settings change whether notices appear and whether output is buffered.
Does the closing PHP tag matter?
In files containing only PHP, leaving it out is recommended because trailing whitespace after it becomes output.
Get notified when this fix updates
Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.
यह पेज सामान्य जानकारी है, जिसे हमारी संपादकीय टीम ने लिखा है और दस्तावेज़ीकरण बदलने पर अपडेट किया जाता है। सर्वर और प्रदाता अलग-अलग होते हैं, इसलिए बदलाव पहले सुरक्षित तरीके से जाँचें। देखें: संपादकीय नीति और अस्वीकरण.