Question
I need a PHP session to remain active for 30 minutes and then be destroyed automatically. What is the correct way to implement a 30-minute session timeout in PHP?
Short Answer
By the end of this page, you will understand how PHP sessions work, how to implement a 30-minute inactivity timeout, how to destroy expired sessions safely, and how this pattern is used in real applications.
Concept
PHP sessions let a server remember information about a user across multiple requests. For example, after a user logs in, you can store their user ID in $_SESSION so they stay authenticated while browsing.
A common requirement is to expire a session after a period of inactivity, such as 30 minutes. This is important for:
- Security: prevents abandoned accounts from staying logged in
- Privacy: protects users on shared devices
- Control: lets the application define its own timeout rules
A key detail is that PHP does not automatically mean "30 minutes of inactivity" just because a session exists. There are two related but different ideas:
- Session cookie lifetime: how long the browser keeps the session cookie
- Application timeout logic: how your code decides whether a user has been inactive too long
For most login systems, the best approach is to store the user's last activity time in $_SESSION, then on each request:
- Start the session
- Check the time since the last activity
- If more than 30 minutes have passed, destroy the session
- Otherwise, update the activity timestamp
This gives you reliable control over session expiration in your application.
Mental Model
Think of a PHP session like a visitor badge at an office.
- The badge identifies the visitor
- The office security desk keeps a record of when the visitor was last active
- If the visitor has been inactive for too long, the badge is invalidated
In PHP:
- The badge is the session ID stored in the browser cookie
- The security record is the data in
$_SESSION - The inactivity rule is your timeout check using a timestamp
So instead of hoping the badge disappears on its own, your application actively checks whether the badge should still be valid.
Syntax and Examples
The usual pattern is:
<?php
session_start();
$timeoutDuration = 1800; // 30 minutes = 1800 seconds
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY']) > $timeoutDuration) {
session_unset();
session_destroy();
}
$_SESSION['LAST_ACTIVITY'] = time();
What this does
session_start()loads the current session$_SESSION['LAST_ACTIVITY']stores the timestamp of the user's last requesttime()returns the current Unix timestamp in seconds- If the difference is greater than
1800, the session is cleared and destroyed - If not expired, the last activity time is updated
A more complete example
<?php
session_start();
$timeoutDuration = 1800;
if (isset($_SESSION['LAST_ACTIVITY'])) {
$inactiveTime = time() - $_SESSION['LAST_ACTIVITY'];
if ($inactiveTime > $timeoutDuration) {
session_unset();
session_destroy();
header('Location: login.php?expired=1');
exit;
}
}
$_SESSION['LAST_ACTIVITY'] = time();
This version redirects the user to a login page after the session expires.
Optional: regenerate session ID after login
<?php
session_start();
// After successful login
session_regenerate_id(true);
$_SESSION['user_id'] = 123;
$_SESSION['LAST_ACTIVITY'] = time();
This improves security by reducing session fixation risk.
Step by Step Execution
Consider this code:
<?php
session_start();
$timeoutDuration = 1800;
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY']) > $timeoutDuration) {
session_unset();
session_destroy();
exit('Session expired');
}
$_SESSION['LAST_ACTIVITY'] = time();
echo 'Session is active';
Example walkthrough
Assume:
$_SESSION['LAST_ACTIVITY'] = 1000- current
time()is2500
Now step through it:
session_start()opens the existing session$timeoutDurationis set to1800- PHP checks whether
$_SESSION['LAST_ACTIVITY']exists- It does, and its value is
1000
- It does, and its value is
- PHP calculates inactivity:
2500 - 1000 = 1500
- PHP compares
1500 > 1800- This is
false
- This is
- The session is not destroyed
$_SESSION['LAST_ACTIVITY']is updated to the current time,2500- The script prints
Session is active
If the user was inactive too long
Assume instead:
$_SESSION['LAST_ACTIVITY'] = 1000- current
time()is2905
Then:
2905 - 1000 = 19051905 > 1800istrue- PHP clears session data with
session_unset() - PHP destroys the session with
session_destroy() - The script exits with
Session expired
That is the core timeout logic.
Real World Use Cases
This pattern is used in many real applications:
- Admin dashboards: log out admins after inactivity for better security
- Banking or billing apps: expire sessions quickly to reduce risk
- School or library computers: prevent the next user from accessing someone else's account
- Internal company tools: enforce inactivity timeouts for compliance rules
- Customer portals: protect personal data when users leave a tab open
Example scenarios
- A user logs into a CMS and walks away from their laptop
- An employee accesses payroll information from a shared machine
- A support agent leaves an authenticated dashboard open during a break
In each case, inactivity timeout helps protect the account even if the browser stays open.
Real Codebase Usage
In real PHP projects, session expiration is usually handled in a shared file that runs on every protected page.
Common pattern: authentication guard
<?php
session_start();
$timeoutDuration = 1800;
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY']) > $timeoutDuration) {
session_unset();
session_destroy();
header('Location: login.php?expired=1');
exit;
}
$_SESSION['LAST_ACTIVITY'] = time();
This is often placed in a file such as auth.php and included at the top of protected pages.
Patterns developers commonly use
- Guard clauses: immediately redirect if the user is not logged in
- Early return or exit: stop processing as soon as the session is invalid
- Centralized validation: keep timeout logic in one reusable file
- Session regeneration: call
session_regenerate_id(true)after login - Redirect with reason: add query parameters like
?expired=1 - Role checks: combine timeout checks with permissions like admin/user
Important note
Many codebases use both:
- application-level inactivity checking with
$_SESSION['LAST_ACTIVITY'] - server/session configuration for cookie and garbage collection settings
The application-level check is the most direct and predictable way to enforce a 30-minute idle timeout.
Common Mistakes
1. Confusing cookie lifetime with inactivity timeout
Setting a cookie lifetime does not always mean the user is logged out after 30 minutes of inactivity.
Broken idea:
<?php
session_set_cookie_params(1800);
session_start();
Why this is not enough:
- It affects the browser cookie lifetime
- It does not by itself check user inactivity in your app logic
- A session may still exist on the server until garbage collection removes it
2. Forgetting to call session_start()
Broken code:
<?php
if (isset($_SESSION['LAST_ACTIVITY'])) {
// ...
}
Fix:
<?php
session_start();
You must start the session before reading or writing $_SESSION.
3. Updating the activity time before checking expiration
Broken code:
<?php
session_start();
$_SESSION['LAST_ACTIVITY'] = time();
if ((time() - $_SESSION['LAST_ACTIVITY']) > 1800) {
session_destroy();
}
Why it fails:
- You overwrite the old timestamp first
- The time difference becomes nearly zero
- The session never expires
4. Destroying the session without stopping the script
Broken code:
<?php
session_start();
session_destroy();
echo 'Welcome back';
Fix:
<?php
session_start();
session_unset();
session_destroy();
exit;
After destroying a session, stop further processing or redirect immediately.
5. Assuming session.gc_maxlifetime alone solves it
This setting controls how long session data may remain before garbage collection, but it is not a precise per-user inactivity timeout.
Use app logic for reliable expiration.
6. Not clearing authentication data properly
If your app stores login information in $_SESSION, make sure it is removed when the session expires.
Typical safe approach:
<?php
session_unset();
session_destroy();
Comparisons
| Approach | What it does | Good for | Limitation |
|---|---|---|---|
$_SESSION['LAST_ACTIVITY'] check | Tracks inactivity in application code | Reliable idle timeout | Must run on each protected request |
session_set_cookie_params() | Sets session cookie lifetime | Controlling browser cookie duration | Not enough alone for inactivity logout |
session.gc_maxlifetime | Defines server-side session data lifetime | Server configuration | Garbage collection is not exact timing |
| Manual logout only | Ends session when user clicks logout | Basic systems | No automatic expiration |
Inactivity timeout vs absolute timeout
| Type | Meaning |
|---|---|
| Inactivity timeout | Log out after no activity for a period, such as 30 minutes |
| Absolute timeout | Log out after a fixed total time, even if the user is active |
Most apps asking for "expire after 30 minutes" actually mean 30 minutes of inactivity.
session_unset() vs session_destroy()
| Function | Purpose |
|---|---|
session_unset() | Removes all session variables |
session_destroy() | Destroys the session data on the server |
Developers often use both together when logging a user out or expiring a session.
Cheat Sheet
session_start();
$timeoutDuration = 1800; // 30 minutes
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY']) > $timeoutDuration) {
session_unset();
session_destroy();
header('Location: login.php?expired=1');
exit;
}
$_SESSION['LAST_ACTIVITY'] = time();
Quick rules
- Call
session_start()before using$_SESSION - Store last activity with
$_SESSION['LAST_ACTIVITY'] = time() - Check inactivity before updating the timestamp
- Use
1800seconds for 30 minutes - After destroying the session,
exitor redirect immediately - Put this logic on every protected page or in a shared auth file
Useful functions
session_start()— starts or resumes a sessiontime()— gets current Unix timestampsession_unset()— clears session variablessession_destroy()— destroys session datasession_regenerate_id(true)— creates a new session ID securely
Good default flow
- Start session
- Check if user is logged in
- Check inactivity timeout
- Destroy expired session if needed
- Update last activity time
- Continue normal page logic
FAQ
How do I automatically log out a PHP user after 30 minutes?
Store the last activity time in $_SESSION, compare it to time() on each request, and destroy the session if the difference is greater than 1800 seconds.
Is session.gc_maxlifetime enough to expire a PHP session?
No. It affects how long session data may remain on the server, but it is not a reliable way to enforce a precise inactivity timeout for each user.
Should I use session_unset() and session_destroy() together?
Yes, that is a common and safe pattern. session_unset() clears session variables, and session_destroy() removes the session data.
What is 30 minutes in PHP session timeout code?
Use 1800 seconds, because 30 × 60 = 1800.
Where should I put PHP session timeout logic?
Usually in a shared file included at the top of all protected pages, such as auth.php or bootstrap.php.
Does user activity in another browser tab keep the session alive?
Yes. If another tab makes a request to the server before the timeout, your LAST_ACTIVITY value will be updated and the session stays active.
Should I regenerate the session ID after login?
Yes. Using session_regenerate_id(true) after a successful login is a good security practice.
Mini Project
Description
Build a small PHP page protection system that logs users out after 30 minutes of inactivity. This demonstrates how real applications protect authenticated pages using session checks and redirects.
Goal
Create a protected page that only logged-in users can access, and automatically expire the session after 30 minutes of inactivity.
Requirements
- Start a PHP session and simulate a logged-in user.
- Store the user's last activity time in the session.
- On each page load, check whether more than 1800 seconds have passed.
- If the session has expired, destroy it and redirect to a login page.
- If the session is still valid, update the last activity timestamp and show protected content.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.