Location redirect working :(

A location redirect is not working when in a hosting environment.

It works fine in my local development set up but when I deploy it to my web host, it breaks slightly with the location redirecting taking the user to a page saying “Redirecting to: index.php.” instead.

As a workaround so users could still navigate to their dashboard I added a link so that mesage reads “Redirecting to: index.php. Click here to go to your dashboard.”

I have read various sources on the web that say it’s probably because there is stuff going on in this code after the redirect but I can’t find a way to chop it and seperate out the redirect to get it to work as it should - that is, users login and are taken automatically to an admin area (rather than the intermediary page).

My login script code in full:

<?php 
     
    // This variable will be used to re-display the user's username to them in the 
    // login form if they fail to enter the correct password.  It is initialized here 
    // to an empty value, which will be shown if the user has not submitted the form. 
    $submitted_username = ''; 
     
    // This if statement checks to determine whether the login form has been submitted 
    // If it has, then the login code is run, otherwise the form is displayed 
    if(!empty($_POST)) 
    { 
        // This query retreives the user's information from the database using 
        // their username. 
        $query = " 
            SELECT 
                id, 
                username, 
                password, 
                salt, 
                email 
            FROM users 
            WHERE 
                username = :username 
        "; 
         
        // The parameter values 
        $query_params = array( 
            ':username' => $_POST['username'] 
        ); 
         
        try 
        { 
            // Execute the query against the database 
            $stmt = $db->prepare($query); 
            $result = $stmt->execute($query_params); 
        } 
        catch(PDOException $ex) 
        { 
            // Note: On a production website, you should not output $ex->getMessage(). 
            // It may provide an attacker with helpful information about your code.  
            die("Failed to run query: " . $ex->getMessage()); 
        } 
         
        // This variable tells us whether the user has successfully logged in or not. 
        // We initialize it to false, assuming they have not. 
        // If we determine that they have entered the right details, then we switch it to true. 
        $login_ok = false; 
         
        // Retrieve the user data from the database.  If $row is false, then the username 
        // they entered is not registered. 
        $row = $stmt->fetch(); 
        if($row) 
        { 
            // Using the password submitted by the user and the salt stored in the database, 
            // we now check to see whether the passwords match by hashing the submitted password 
            // and comparing it to the hashed version already stored in the database. 
            $check_password = hash('sha256', $_POST['password'] . $row['salt']); 
            for($round = 0; $round < 65536; $round++) 
            { 
                $check_password = hash('sha256', $check_password . $row['salt']); 
            } 
             
            if($check_password === $row['password']) 
            { 
                // If they do, then we flip this to true 
                $login_ok = true; 
            } 
        } 
         
        // If the user logged in successfully, then we send them to the private members-only page 
        // Otherwise, we display a login failed message and show the login form again 
        if($login_ok) 
        { 
            $user_name = $_POST['username'];

            // Create ativity item using 'last ID'
            $sql = "INSERT INTO users_logins(user_name)
            VALUES('$user_name')";

            $db->exec($sql);

            // Here I am preparing to store the $row array into the $_SESSION by 
            // removing the salt and password values from it.  Although $_SESSION is 
            // stored on the server-side, there is no reason to store sensitive values 
            // in it unless you have to.  Thus, it is best practice to remove these 
            // sensitive values first. 
            unset($row['salt']); 
            unset($row['password']); 
             
            // This stores the user's data into the session at the index 'user'. 
            // We will check this index on the private members-only page to determine whether 
            // or not the user is logged in.  We can also use it to retrieve 
            // the user's details. 
            $_SESSION['user'] = $row; 
             
            // Redirect the user to the private members-only page. 
            header("Location: index.php"); 
            die("Redirecting to: index.php. <a href='index.php'>Click here</a> to go to your dashboard."); 
        } 
        else 
        { 
            // Tell the user they failed 
            print("<div class='alert alert-danger' role='alert' style='margin-top:0;'>
    <a href='#' class='close' data-dismiss='alert'>&times;</a>
        You need to log in to do that!
    </div>"); 
             
            // Show them their username again so all they have to do is enter a new 
            // password.  The use of htmlentities prevents XSS attacks.  You should 
            // always use htmlentities on user submitted values before displaying them 
            // to any users (including the user that submitted them).  For more information: 
            // http://en.wikipedia.org/wiki/XSS_attack 
            $submitted_username = htmlentities($_POST['username'], ENT_QUOTES, 'UTF-8'); 
        } 
    } 
     
?> 

The login code where the redirect is present:

// If the user logged in successfully, then we send them to the private members-only page 
        // Otherwise, we display a login failed message and show the login form again 
        if($login_ok) 
        { 
            $user_name = $_POST['username'];

            // Create ativity item using 'last ID'
            $sql = "INSERT INTO users_logins(user_name)
            VALUES('$user_name')";

            $db->exec($sql);

            // Here I am preparing to store the $row array into the $_SESSION by 
            // removing the salt and password values from it.  Although $_SESSION is 
            // stored on the server-side, there is no reason to store sensitive values 
            // in it unless you have to.  Thus, it is best practice to remove these 
            // sensitive values first. 
            unset($row['salt']); 
            unset($row['password']); 
             
            // This stores the user's data into the session at the index 'user'. 
            // We will check this index on the private members-only page to determine whether 
            // or not the user is logged in.  We can also use it to retrieve 
            // the user's details. 
            $_SESSION['user'] = $row; 
             
            // Redirect the user to the private members-only page. 
            header("Location: index.php"); 
            die("Redirecting to: index.php. <a href='index.php'>Click here</a> to go to your dashboard."); 
        } 
        else 
        { 
            // Tell the user they failed 
            print("<div class='alert alert-danger' role='alert' style='margin-top:0;'>
    <a href='#' class='close' data-dismiss='alert'>&times;</a>
        You need to log in to do that!
    </div>"); 
             
            // Show them their username again so all they have to do is enter a new 
            // password.  The use of htmlentities prevents XSS attacks.  You should 
            // always use htmlentities on user submitted values before displaying them 
            // to any users (including the user that submitted them).  For more information: 
            // http://en.wikipedia.org/wiki/XSS_attack 
            $submitted_username = htmlentities($_POST['username'], ENT_QUOTES, 'UTF-8'); 
        } 
    } 

Any help would be appreciated as it’s an issue I have been trying to fix for a while.

Some advice to help make this code a little clearer and more inline with modern standards:

  1. Passwords should be hashed using *password_hash('my_password_here', PASSWORD_DEFAULT)*
  2. Passwords should be validated using *password_verify($form_password, $hashed_password_from_the_database)*
  3. Use PDO prepare properly. It will automatically escape user input.
$sql = "INSERT INTO users_logins(user_name) VALUES(:user)";
$db->perpare($sql);
$db->execute(['user' => $user_name]);
  1. Using the header('Location: /index.php') method to redirect depends on the implementation of redirects in the browser and how the server presents the redirect (301, 302, 303?). To avoid this pitfall, most websites use a javascript based redirect. This means the login form is reloaded but shows a success message. In addition, a small bit of javascript is added to the page:
window.setTimeout(() => {
    window.location.replace("http://mysite.com");
}, 2000);

This will wait 2000 milliseconds (aka 2 seconds) and then redirect the site to some other page.