HttpOnly JWT Cookie Vanishes on Page Refresh in Local React App with Remote Backend

I’m working on a React application hosted at http://localhost:5173 alongside a PHP backend that’s deployed to a live server. The authentication process utilizes JWT tokens, which are stored in HttpOnly cookies.

Upon successful login, the backend sets the cookie correctly and I can see it in my browser’s developer tools under Application → Cookies. However, the issue arises when I refresh the page; the cookie disappears entirely and the user is logged out again.

Here’s the PHP code I use to create the authentication cookie:

setcookie('jwt_token', $token, [
    'expires' => time() + 3600 * 24,
    'httponly' => true,
    'samesite' => 'Lax',
    'secure' => false,
]);

header('Content-Type: application/json; charset=UTF-8');
header('Access-Control-Allow-Origin: http://localhost:5173');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');

header('X-XSS-Protection: 1; mode=block');
header('X-Frame-Options: SAMEORIGIN');
header('X-Content-Type-Options: nosniff');

What could be causing the HttpOnly cookie to be deleted after refreshing the page in this development environment? How can I ensure that the JWT cookie stays intact when my frontend is on localhost while the backend runs on a remote server?

Your cookies are disappearing because of domain mismatch between localhost and your remote backend. Browsers block cookies from remote domains during page reloads - it’s a security thing. I’ve hit this same issue before. Fix it by setting the domain parameter in your setcookie function to match your backend’s domain, or just use the same domain for both frontend and backend in development. What worked for me was adding a token refresh check on page load that re-authenticates if the cookie’s gone. Also check your browser isn’t blocking third-party cookies - that’ll mess with cross-origin cookie storage during development.

Interesting setup! Are you using different ports or protocols? That might confuse the browser. Does this happen in all browsers or just specific ones? Chrome’s really picky about localhost cookies. Check the network tab when you refresh - see if the cookie’s actually getting sent in the request headers.

sounds like a classic cors cookie issue tbh. try adding path parameter to your setcookie call and make sure its set to /. also double check your fetch requests are using credentials: 'include' - without that the browser wont send cookies cross-origin even if they exist. had similar headaches with local dev setups before