Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

A JavaScript redirect normally does not delete a PHP session. The destination request must send the same session cookie, and the destination PHP script must call session_start() before reading $_SESSION. Check those two conditions first.

PHP stores session data on the server and usually identifies it with a browser cookie such as PHPSESSID. A redirect—whether triggered by JavaScript or PHP—does not directly carry $_SESSION data to the next page.

The correct session-and-redirect pattern

Use session_start() before assigning or reading session values, assign the value before redirecting, and stop the script after the redirect.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

save.php

<?php
declare(strict_types=1);

session_start();

$_SESSION['flash'] = 'Saved successfully';

header('Location: /result.php', true, 302);
exit;

result.php

<?php
declare(strict_types=1);

session_start();

$message = $_SESSION['flash'] ?? null;
unset($_SESSION['flash']);

echo htmlspecialchars((string) $message, ENT_QUOTES, 'UTF-8');

Every PHP request that reads or writes $_SESSION must start or resume the session, unless your application has explicitly enabled automatic session startup. See PHP’s session_start() documentation.

JavaScript redirects versus PHP redirects

These two redirects ultimately cause the browser to request another URL:

window.location.href = '/dashboard.php';
<?php
header('Location: /dashboard.php');
exit;

A PHP redirect is sent as an HTTP response, normally with a Location header. A JavaScript redirect occurs after the current response reaches the browser. Neither method transports the PHP session array itself. The browser must retain the session cookie and send it with the next request.

PHP also documents that a session ID is not automatically placed in the Location header. Changing from window.location.href to location.replace() usually will not fix session persistence; those APIs mainly differ in browser history behavior. The resulting URL and its cookie rules matter.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For an intentional URL, prefer a root-relative path:

window.location.href = '/dashboard.php';

A relative URL such as dashboard.php is resolved against the current document path and can unexpectedly target a different directory.

First: verify the cookie in browser developer tools

Do not guess whether JavaScript “lost” the session. Inspect the actual HTTP requests.

  1. Open your browser’s developer tools and select Network.
  2. Submit the form or perform the login action.
  3. Select the request that writes the session.
  4. In Response Headers, look for a header like Set-Cookie: PHPSESSID=....
  5. Select the request for the redirected destination.
  6. In Request Headers, look for Cookie: PHPSESSID=....
  7. Compare the session ID in the response cookie with the cookie sent to the destination.
  8. In Application or Storage → Cookies, inspect the cookie’s name, domain, path, expiration, Secure, HttpOnly, and SameSite attributes.
What you observe Most likely explanation
No Set-Cookie on the session-writing response session_start() did not run, output was sent first, or PHP could not initialize the session.
Set-Cookie exists but no cookie is stored The browser rejected it because of its domain, path, Secure, SameSite, or another cookie policy.
The cookie is stored but absent from the destination request The destination does not match the cookie’s host, path, protocol, or browser policy.
The same cookie is sent but PHP sees a new session The session store is unavailable, expired, inconsistent, or different on the destination server.
The same cookie is sent but the expected key is missing The value was not assigned, was overwritten or destroyed, or another request changed the session.

A cookie is not JavaScript state. It is attached to HTTP requests according to its scope and security attributes. See PHP’s session configuration documentation and MDN’s Set-Cookie reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common PHP causes

The destination does not call session_start()

This is the most common mistake:

// login.php
session_start();
$_SESSION['logged_in'] = true;
// dashboard.php
session_start();

if (!($_SESSION['logged_in'] ?? false)) {
    header('Location: /login.php');
    exit;
}

Without session_start(), PHP does not restore the existing session into $_SESSION.

Output was sent before the session started

session_start() may need to send a Set-Cookie header. Headers must be sent before the response body, so start the session before HTML, echo, debugging output, accidental whitespace, or a UTF-8 byte-order mark.

<?php
session_start();

These patterns are unsafe:

<html>
<body>
<?php
session_start();
<?php
echo 'Logging in...';
session_start();

Check PHP logs for Cannot modify header information - headers already sent. You can also temporarily use:

<?php
session_start();

if (headers_sent($file, $line)) {
    error_log("Headers already sent in $file on line $line");
}

PHP’s cookie documentation and setcookie() documentation explain why cookies and other headers must be sent before output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The value is assigned after the redirect

Assign session data before sending the redirect:

session_start();

$_SESSION['message'] = 'Saved successfully';

header('Location: /result.php');
exit;

Code after a redirect is not a reliable place for session changes. Always call exit so later code cannot overwrite the session, emit output, or perform unrelated work.

A long request has not committed the session

PHP sessions are commonly locked while a request has an open session. If the request performs substantial work, explicitly close the session after writing:

session_start();

$_SESSION['message'] = 'Saved successfully';
session_write_close();

header('Location: /result.php');
exit;

Closing the session allows another request from the same browser to access the stored data instead of waiting for the first request to finish. Use this when you no longer need to modify $_SESSION in the current request.

Check URL and cookie scope

www and non-www hosts

example.com and www.example.com are different hosts for cookie purposes. A host-only cookie created on one may not be sent to the other.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For example, avoid creating the session at:

https://example.com/login.php

and redirecting to:

https://www.example.com/dashboard.php

Choose one canonical hostname and use it consistently. If a session genuinely must work across subdomains, configure the domain deliberately:

<?php
session_set_cookie_params([
    'lifetime' => 0,
    'path'     => '/',
    'domain'   => '.example.com',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);
session_start();

Do not broaden the cookie domain for a single-host site. A wider domain exposes the cookie to more subdomains. PHP documents these settings in session_set_cookie_params().

HTTP and HTTPS

A cookie with Secure is sent only over HTTPS. A site that creates a session on HTTP and then navigates inconsistently between HTTP and HTTPS can appear to lose that session. Production applications should use HTTPS consistently.

A typical HTTPS-only configuration is:

<?php
session_set_cookie_params([
    'lifetime' => 0,
    'path'     => '/',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);

session_start();

Use secure => false only for a development site that genuinely runs over HTTP.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cookie path

A cookie scoped to /login/ will not be sent to /dashboard.php. For a site-wide PHP session, the normal path is:

'path' => '/'

PHP’s default session cookie path is /, but application or hosting configuration can override it. Inspect the actual Path attribute in developer tools.

SameSite

SameSite affects cross-site navigation and authentication flows:

  • Lax is a common choice for ordinary same-site applications and permits many top-level cross-site GET navigations.
  • Strict is more restrictive and can omit the cookie in some cross-site navigation contexts.
  • None permits cross-site cookie sending but must be paired with Secure, meaning HTTPS is required.

Do not change to SameSite=None unless cross-site cookies are actually required. The right setting depends on whether your authentication provider, frontend, and PHP application are on the same site.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Duplicate cookies

Old cookies with the same name but different paths or domains can produce confusing results. For example, a browser might have one PHPSESSID for / and another for /admin/. Remove stale cookies for the site, retry the flow, and inspect the new cookie’s attributes.

Subdomains, ports, and separate applications

Suppose login runs at login.example.com and the application runs at app.example.com. Even if the browser sends a cookie to both hosts, the destination may still be unable to read the same session data.

Both applications must agree on the session name and access the same backend. Check for differences in:

  • session_name() and the cookie name;
  • session.save_handler;
  • session.save_path;
  • Redis, database, or custom session-handler configuration;
  • PHP-FPM pools, containers, machines, and file permissions.

Different ports can also indicate different applications or servers. Cookies do not primarily use ports as their scope, but localhost:8000 and localhost:8080 may not share a session backend.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Compare these values in both applications:

<?php
var_dump([
    'save_handler' => ini_get('session.save_handler'),
    'save_path'    => session_save_path(),
    'session_name' => session_name(),
]);

Check PHP and web-server logs for errors such as session_start(): Failed to read session data or session_start(): Failed to write session data. A missing or unwritable session.save_path makes each request appear to receive a new session, even when the cookie is correct.

Session expiration and caching

An immediate failure after navigation is more likely to involve session_start(), cookie scope, output-before-header problems, or incompatible session storage than expiration.

PHP’s documented default for session.gc_maxlifetime with file sessions is 1440 seconds, but it is not an exact user-visible lifetime. Garbage collection is probabilistic, hosting settings can differ, multiple applications may share a session directory, and custom handlers have their own behavior. A failure after a period of inactivity warrants checking these settings.

Caching can also display an old page that makes authentication state look incorrect. PHP’s default session cache limiter is nocache, but application or reverse-proxy settings may override it. Verify the actual network request and server-side session rather than relying on the page currently displayed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

AJAX, login races, and session regeneration

A redirect may only expose a race that began earlier. For example, a login request may write authentication data while JavaScript immediately starts another request. The second request can wait on the session lock, read an older state, or interact badly with session-ID regeneration.

With fetch(), navigate only after the login request has completed successfully:

fetch('/login.php', {
    method: 'POST'
})
.then(response => {
    if (!response.ok) {
        throw new Error('Login request failed');
    }
    window.location.href = '/dashboard.php';
})
.catch(error => {
    console.error(error);
});

For cross-origin fetch requests, cookie credentials and server CORS configuration may also be required. That is separate from a normal top-level browser navigation.

After successful authentication, regenerating the session ID helps prevent session fixation:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php
session_start();

// Validate credentials first.
session_regenerate_id(true);

$_SESSION['user_id'] = $userId;
$_SESSION['authenticated'] = true;
session_write_close();

header('Location: /dashboard.php');
exit;

Do not treat session_regenerate_id(true) as a universal redirect fix. PHP documents race-condition and unstable-network caveats, especially when concurrent requests may still use the old ID. Regenerate at one well-defined point in the authentication flow and avoid firing dependent requests before that flow finishes. See PHP’s session_regenerate_id() and session security management guidance.

Audit code that may clear or replace the session

Search included files, middleware, logout handlers, and authentication logic for:

session_destroy();
$_SESSION = [];
session_unset();
session_id($someOtherId);
session_name($differentName);

A temporary diagnostic can identify whether the ID and session contents change between requests:

<?php
session_start();

error_log(json_encode([
    'script'       => $_SERVER['SCRIPT_NAME'] ?? null,
    'session_id'   => session_id(),
    'session_name' => session_name(),
    'cookie'       => $_COOKIE[session_name()] ?? null,
    'session'      => $_SESSION,
], JSON_PRETTY_PRINT));

Do not log session IDs or complete session contents in production without assessing the security risk. Remove temporary diagnostics when the investigation is complete.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not put the session ID in the URL

A tempting workaround is:

header('Location: /dashboard.php?' . SID);

Prefer a normal cookie-based session:

header('Location: /dashboard.php');
exit;

Session IDs in URLs can leak through browser history, server logs, referrers, screenshots, and copied links. PHP recommends cookie-based sessions and secure session management; disabling cookie-only behavior is deprecated as of PHP 8.4. See PHP’s session configuration and session security documentation.

Final troubleshooting checklist

  • session_start() appears in both the writing and reading scripts.
  • It runs before HTML, echo, whitespace, or other output.
  • The session value is assigned before the redirect.
  • The redirect is followed by exit.
  • The session-writing response contains Set-Cookie.
  • The destination request sends the same session cookie.
  • Hostname and scheme are consistent, including www and HTTPS.
  • The cookie path is / where a site-wide session is intended.
  • The cookie’s Secure and SameSite settings fit the deployment.
  • Both requests use the same session name and storage backend.
  • No code destroys or unexpectedly replaces the session.
  • PHP logs contain no session read, write, or permission errors.
  • Login and dependent AJAX requests are not racing.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.