I have a problem with header()

In my website, all pages which are visible to the user include 3 major files:
the top part in page_header.php
The bottom is page_footer.php
The middle is the page content.

In thr error_log file I constantly get the warning:

PHP Warning: session_start(): Session cannot be started after headers have already been sent in /home4/traderan/public_html/pages/page_header.php on line 2

Here is the content of page_header.php ( the upper part):

<?php
session_start();

if(isset($_SESSION['user_id']) && !empty($_SESSION['user_id'])){
    $user_id = $_SESSION['user_id'];
	
	
	//Get user data from DB
require 'includes/fetch.php';


``

I also get it here:

> PHP Warning:  Cannot modify header information - headers already sent by (output started at /home4/traderan/public_html/includes/reset_request.inc.php:77) in /home4/traderan/public_html/includes/reset_request.inc.php on line 78
Here is file content:


mail($to, $subject, $message, $headers);

header('Location: ../reset_notice.php'); //This is line 78
exit();

I uploaded the file in case you want to see it:
reset_request.inc.php (2.6 KB)

the process stops at this file, Here is a screenshot:

When I refresh the page the header() command is processed and the page is redirected to target page

Do I use header correctly?
I searched the web for solutions for the problem but when I found a sulution e.g. ob_start(), I also found an explanation that this hides the problem - not solving it.
How do I find the source of the problem ?

This error means that some output has been made to the browser before the session_start() call. It likely doesn’t have anything to do with your header() calls. You didn’t show the code of the user visible page that included page_header.php, so it’s hard to diagnose from here. Show that code (the portion before the include), or start looking at that for anything that might be output to the browser. For example “!DOCTYPE” could be a guess of something you’re sending out.

1 Like

The code for any page should be laid out in this general order -

  1. initialization
  2. post method form processing
  3. get method business logic - get/produce data needed to display the page
  4. html document

The session_start() statement should be in the initialization section.

The only redirect you should have is upon successful completion of the post method form processing code and it should be to the exact same URL of the current page to cause a get request for that page. This will prevent the browser from trying to resubmit the form data should that URL get browsed back to or reloaded.

If your form processing and form are on different pages, this results in double the amount of code (assuming you ever complete all the code), a poor User eXperience (UX), and by redirecting around on your site and using get parameters in the url to control what messaging gets displayed, is setting your site op for a phishing attack, where someone can trick one of your users to enter their information on the phishing site, then they can redirect the user to your site and make it look like they just entered their information incorrectly.

Your file name indicates you’re including this file in another file.
The other file has already sent some sort of output to the browser.
Include this file on character 1, line 1, of the other file.

Thank you ChatGPT for the overly wordy answer that contains at least 3 glaring "it’s not that"s that a human would not include.

1 Like

This file is not included in another file. It receives the user email for password reset process.
Anyway how can I include a file in character 1 line 1 of a file when the a php file starts with <?php

Here is the page_header.php file:
page_header.php (5.0 KB)

And the header.php file which is included in the previous one:
header.php (3.7 KB)

Here is index.inc.php, the content of index.php:
index.inc.php (3.6 KB)

and page_footer, the footer of every visible page:
page_footer.php (712 Bytes)

They all have html code, how do U solve the php warning ?

You have two separate problems. By posting them together, you are getting mixed-up solutions.

The first error, concerning the session start, should include where the output is occurring at as part of the error message (there are some php versions that didn’t include this information in the error message.) Are you using at least php8, and is there a second similar message in the error log?

Next, is page_header.php the ‘main’ page that gets requested? It ‘requires’ header.php, but there is nothing that would require index.ini.php or page_footer.php. Where or how are these two files being required?

The most likely cause for the above is the main file/page that gets requested is outputting something before the session start. If the main page is page_header.php, then the problem is most likely because that file has been saved with UTF-8 encoding, with the Byte Order Mark (BOM) characters. If so, the solution is to save the file without the BOM characters.

For the second problem, in the reset_request.inc.php code (which is the target of a post method form submission), line 76 in the attached code is the mail() call. Line 77 is empty, and line 78 is the header() call. The most likely cause of this problem is the mail() call is producing a php error, that is being output on the page. What does the ‘view source’ of the page show and is there a ‘mail’ related message in the error log?

As to why the reset_request_inc.php code may ‘work’ when you refresh the page, what exact URL does it redirect to? I suspect it is not reset_notice.php or perhaps there is some code on reset_notice.php that’s redirecting to another page. This is one of the problems with all these redirects.

If you use the suggestions in my first reply in this thread, you will end up with simpler code, that provides a better user experience (you can display user/validation errors when you redisplay the form and populate the form fields with existing data so that the user doesn’t need to keep reentering things over and over), that’s easier to write, easier to secure, and easier to maintain.

I found where the error occurs but I can’t see what causes it
The page reset_password.php looks like that:

<?php
include_once "pages/page_header.php";
include_once "pages/reset_password.inc.php";
include_once "pages/page_footer.php";
?>

Here is page_header.php
page_header.php (5.0 KB)
Here is header.php which is included in page+header.php
header.php (3.6 KB)
And here is fetch.php with the relevant data
fetch.php (4.6 KB)

Here is reset_password.inc.php:

<?php
if(isset($_GET['reset']) && !empty($_GET['reset']) && !empty($_GET['reset']) =="1"){
     $message ="Check Your email";
	 include_once 'pages/error_message.php';
  }
?><section class="main-container">
     <div id="" class="main-wrapper">
		
			<h2>Reset Your Password</h2>
	     	<h2><small>An Email will be sent to you with instruction how to reset your password</small></h2>
			<form class = "utility_form" method="post" action="includes/reset_request.inc.php">
			<h3>My email:</h3> <input type="text" name="email" placeholder="Enter your email address....">
				<button type="submit" name="reset-request-submit">
				  Click Here To Start Password Reset by email
			    </button>
            </form>
	 </div>

</section>

And here is reset_request.inc.php, where the form redirects to

<?php
session_start();
//https://www.youtube.com/watch?v=wUkKCMEYj9M&ab_channel=DaniKrossing
if(isset($_POST['reset-request-submit'])) {

    require 'fetch.php';
// Get email geven by the user
	$userEmail = $_POST['email'];
	
// Check if email belongs to a registered user
    $result = check_email_validity($userEmail);
 
    if($result <1  ){
	   header('Location: ../reset_password.php?error="no_user_with_this_email"');
	   exit();
      }
	
// Create a 8 bit random number and turn it to HEX
	$selector = bin2hex(random_bytes(8));
	
// Create a 32 bite token	
	$token = random_bytes(32);
	$validator = bin2hex($token);
// Create a link which will be sent to the user
	$url = "www.traderanalytics1.com/create_new_password.php?selector=".$selector.   
	        "&validator=".$validator;

//Set expiry time for the token 1 hour ahead from now
	$expires = date("U") + 1800;


// Check if user has tokens in pwdReset table
$rowcount = check_user_tokens_in_db_table($userEmail);

// if function returns false / negative number / non numeric value - there's an error
if($rowcount < 0 || is_numeric($rowcount) != true){

     header('Location: ../reset_password.php?error="erro_check_token"');
	 exit();
    }

// if function returns positive number delete data related to given email
if($rowcount > 0){
	
	 $result = delete_user_tokens_in_db_table($userEmail);
	 
     if($result == false){
        header('Location: ../reset_password.php?error="delete_error"');
	    exit();
    }
  }// End if

// After deleteng previous reset data from table / verifying no previous token exist -      We insert new date to be sent to user
if($rowcount == 0){
$result = insert_tokens_for_pwdreset($userEmail, $selector, $validator, $expires);

	if($result == false){ //error on insertind rwd reset info into table
     header('Location: ../reset_password.php?error="insert_error"');
	 exit();
   } else {
// Send email to the user
	$to = $userEmail;

	$subject = 'Reset your password for this website';

	$message = '<p>We received a password reset request. The link to reset
	               your password is below.</br>If you did not make this request
				   - ignore this email
				</p><p>Here is your password reset link:< /br>';
	$message .='<a href="'.$url.'">'.$url.'</a></p>';

	$headers = "From: support <support@traderanalytics1.com>\r\n";
	$headers .= "Reply-To: support@traderanalytics1.com\r\n";
	$headers .= "Content-type:text/html\r\n";

	mail($to, $subject, $message, $headers);
    header('Location: ../reset_password.php?reset="1"');
    exit();
   }
  }
}else {
		header("Location: ../index.php");
        exit();
	}

The process stops on reset_request.inc.php and I get this message in error log file:

PHP Warning: session_start(): Session cannot be started after headers have already been sent in /home4/traderan/public_html/pages/page_header.php on line 2

Here is a print screen


And a print screen of page view source

When I refresh the browser, I receive the email for password reset

So where is the problem in the code ?

Possibly unrelated to much, but I notice in page_header.php you have this line which is missing a quote at the end:

<link rel="stylesheet" type="text/css" href="includes/basic.css?ver=301
1 Like

As to the session_start error -

Now that you have finally shown how you are building the main page, it appears that the downloaded page_header.php file doesn’t have the BOM characters (though uploading the file to a site can remove them.) This leaves reset_password.php. Is it saved as a UTF-8 encoded file with the BOM characters? Is there anything in reset_password.php before the opening <?php tag?

As to the reset_request.inc.php operation. Hidden in that unnecessary wall of code, which I doubt you will apply the suggestions to simplify it, you have a logic mistake. You are likely testing this without actually resetting the password each time? This results in a $rowcount value > 0. The code executes the if($rowcount > 0){ logic to delete any existing tokens, but since $rowcount is not equal to 0 at that time, the code inserting the tokens, building the email, and sending the email is never executed. When you reload the page, the $rowcount is now 0, and the logic runs as expected.

In this code, you should forget about getting the $rowcount value at all, simply attempt to delete any tokens (if there are none, nothing will happen, if there are any, they will be deleted), insert the newly generated tokens, build the email, and send it.

You also need to test the value returned by the mail() call and handle it in the code, setting up an error message for the user if it fails (logging all the available information about the request so that you will know it is failing), and only display the success content if it was successful (which doesn’t actually mean the that email was sent.)

mabismad, thank you for your eye opening explanations,

No, it doesn’t. I’m not sure I understand what are BOM characters. I see it here for the first time,
In the db connect file I was told I should add this row od code:
$db->exec("SET NAMES 'utf8'");
When I uploaded my website to the hosting server and nothing worked, I made changes and removed it as I didn’t see it in any DB connection code on the web.
Is this the BOM characters ?
is it enough?
How and where do I define BOM characters in page_header.php ?
Is this the cause for the constant PHP warning

In my DB tables, all varchar type data are defined as utf8_unicode_ci
Does it have anything to do with BOM characters ? I apologise for my ignorance but can I get an explanation about it or a ling to a good and clear explanation ?

BOM (Byte Order Mark) characters are sometimes added in to the very start of your text files (the files that contain your PHP scripts, HTML code etc.) by the editor that you use to create and modify those files. They’re used in situations where you might be storing the file in a way where more than one byte represents a character, and they’re used to show which order the multiple bytes for each character are stored in.

Because they are at the very start of the file, when you execute such a file, those characters will be sent to the browser where they won’t display (just like if you had a blank space or a newline right at the start of the file), but they will cause headers to be sent.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.