浏览器关闭时自动登出并记录登出时间至MySQL的实现方案问询
Hey there! Let's work through your problem of logging a user's logout time to MySQL when they close their browser. I totally get the frustration with window.onbeforeunload—it's inconsistent across browsers, doesn't let you run custom logic reliably on refresh, and modern browsers have locked down custom prompt messages. Let's break down what's wrong with your current code, and then walk through a more reliable approach.
First, why your test code isn't working as expected
Your current setup uses onbeforeunload to return a custom message, but all major browsers (Chrome, Firefox, Edge) no longer display custom messages for this event—they show a generic prompt instead. Also, this event fires on both browser close and page refresh, which means you'd accidentally log a "logout" every time the user hits F5, which isn't what you want.
A more reliable solution
The key here is to:
- Distinguish between actual browser close/tab close and other actions (refresh, clicking a link to navigate away)
- Send a reliable request to your backend to log the time—regular AJAX calls might get canceled by the browser during page unload, so we'll use a dedicated API for this.
Here's a step-by-step implementation:
1. Frontend JavaScript (with proper event handling)
We'll use the pagehide event (more consistent than beforeunload for unload scenarios) and navigator.sendBeacon()—a browser API designed specifically to send data during page unload without blocking the process.
<!DOCTYPE html> <html> <body> <p>Close this window, press F5 or click on the link below to test.</p> <a href="https://www.google.com">Test by redirecting</a> <script> // Track if the user is navigating away normally (e.g., clicking a link) let isNormalNavigation = false; // Mark normal navigation when user clicks a link document.addEventListener('click', (e) => { if (e.target.tagName === 'A' && e.target.href) { isNormalNavigation = true; } }); // Mark refresh as "non-logout" action window.addEventListener('load', () => { if (navigation.type === 'reload') { isNormalNavigation = true; } }); // Handle page unload (close/tab switch) window.addEventListener('pagehide', (event) => { // Skip if it's a normal navigation or refresh if (!isNormalNavigation && !event.persisted) { // Prepare data to send (replace with your actual user ID logic) const logoutData = new FormData(); logoutData.append('user_id', '123'); // Replace with logged-in user's ID // Send data to backend using sendBeacon (reliable for unload scenarios) navigator.sendBeacon('/api/logout-time', logoutData); } }); // Optional: Fallback with beforeunload for older browsers window.addEventListener('beforeunload', (event) => { if (!isNormalNavigation) { // Modern browsers ignore custom messages, so we just prevent default event.preventDefault(); event.returnValue = ''; // Required for some older browsers } }); </script> </body> </html>
2. Backend Code (MySQL example with PHP)
You'll need a backend endpoint to receive the logout time and save it to your database. Here's a simple PHP example:
<?php // /api/logout-time.php // Connect to your MySQL database (replace with your credentials) $dbHost = 'localhost'; $dbUser = 'your_username'; $dbPass = 'your_password'; $dbName = 'your_database'; $conn = new mysqli($dbHost, $dbUser, $dbPass, $dbName); if ($conn->connect_error) { die("Database connection failed: " . $conn->connect_error); } // Validate user ID (always sanitize/validate input!) $user_id = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0; if ($user_id === 0) { exit("Invalid user ID"); } $logout_time = date('Y-m-d H:i:s'); // Prepare and execute the update query (use prepared statements to prevent SQL injection) $stmt = $conn->prepare("UPDATE users SET logout_time = ? WHERE id = ?"); $stmt->bind_param("si", $logout_time, $user_id); $stmt->execute(); $stmt->close(); $conn->close(); ?>
Key Notes
navigator.sendBeacon()is crucial: Unlike regular AJAX/fetch calls, this method ensures the browser sends the request in the background, even after the page starts unloading. It won't block the user from closing the tab.- Distinguishing actions: We use
isNormalNavigationto skip logging when the user clicks a link or refreshes the page. Thenavigation.typecheck catches refreshes, and the click listener catches link navigations. - No 100% guarantee: There's still a tiny edge case (e.g., user force-quits the browser entirely) where the request might not send, but this is the most reliable approach available today.
- Security: Always validate and sanitize user input (we used
intval()for the user ID here) and use prepared statements to prevent SQL injection. Also, make sure to authenticate the request (e.g., check if the user is still logged in via session or token) to avoid fake logout logs.
内容的提问来源于stack exchange,提问作者Muhammad Al Nahtta

