databases storage and caching
Session Storage Backends
Sessions let a PHP application remember state between HTTP requests. The browser usually keeps a small session ID cookie, while the actual session data lives on the server in a storage backend such as files, Redis, Memcached, or a database.
The main skill is choosing storage that matches the deployment. A single-server application can often use file sessions. A load-balanced application needs a backend every app server can reach, or users may appear logged out when their next request lands on a different server.
What A Session Stores
Session data should be small and temporary. It is useful for values such as a logged-in user ID, CSRF token, flash message, or checkout step. It is not a place for whole user profiles, shopping catalogues, large API responses, or source-of-truth business data.
<?php
declare(strict_types=1);
function sessionPayloadForLogin(int $userId): array
{
return [
'user_id' => $userId,
'authenticated_at' => time(),
'flash_messages' => [],
];
}
$payload = sessionPayloadForLogin(42);
echo $payload['user_id'] . PHP_EOL;
echo array_key_exists('flash_messages', $payload) ? 'has flash storage' : 'missing flash storage';
echo PHP_EOL;
// Prints:
// 42
// has flash storage
The session should usually store an ID, then load fresh user details from the database when needed. That avoids stale roles, stale account status, and oversized sessions.
The Cookie Is Not The Whole Session
The browser cookie normally stores only the session ID. The server uses that ID to read the session payload from the backend.
<?php
declare(strict_types=1);
function sessionLookupPlan(string $sessionId, string $backend): string
{
return sprintf('Read session %s from %s storage.', $sessionId, $backend);
}
echo sessionLookupPlan('abc123', 'Redis') . PHP_EOL;
// Prints:
// Read session abc123 from Redis storage.
This is why stealing a session cookie is serious: the cookie can point to server-side authenticated state. Protect it with Secure, HttpOnly, and an appropriate SameSite setting.
Common Backends
File sessions are PHP's common default. They are simple and fine for one server, but they do not naturally work across multiple app servers unless the files are on shared storage.
Redis sessions are common for production applications with several app servers. Redis is fast, centralised, and supports expiry well. It is often the strongest default for horizontally scaled PHP apps.
Database sessions are useful when the team wants persistence and queryability, or when Redis is not available. They are usually slower than Redis and can add write load to the main database.
Memcached sessions can work for temporary session data, but the team must be comfortable with eviction behaviour. If losing a session unexpectedly would be unacceptable, choose carefully.
<?php
declare(strict_types=1);
function recommendSessionBackend(int $appServerCount, bool $redisAvailable): string
{
if ($appServerCount === 1) {
return 'files';
}
if ($redisAvailable) {
return 'redis';
}
return 'database';
}
echo recommendSessionBackend(3, true) . PHP_EOL;
// Prints:
// redis
The backend choice is partly technical and partly operational. It depends on what the team can run, monitor, back up, and debug.
Load Balancers And Sticky Sessions
Some load balancers support sticky sessions, where the same user is sent to the same app server. Sticky sessions can make file sessions appear to work on multiple servers, but they are fragile. If a server fails or traffic is rebalanced, users can lose access to the file that held their session.
A shared backend is usually clearer. Any app server can handle the next request because all servers read from the same session store.
<?php
declare(strict_types=1);
function sessionReachabilityCheck(string $backend, int $appServerCount): string
{
if ($backend === 'files' && $appServerCount > 1) {
return 'risk: file sessions are local unless storage is shared';
}
return 'ok: every app server can use the session backend';
}
echo sessionReachabilityCheck('files', 4) . PHP_EOL;
// Prints:
// risk: file sessions are local unless storage is shared
This is one of the most common real deployment mistakes with PHP sessions.
Expiry And Garbage Collection
Sessions need expiry. Old sessions should not live forever, and logged-out sessions should be removed or invalidated.
With Redis, expiry is usually handled with key TTLs. With database sessions, the application often stores a last_activity or expires_at column and deletes old rows. With file sessions, PHP has session garbage collection settings, but the exact behaviour depends on configuration and traffic.
<?php
declare(strict_types=1);
function sessionExpirySeconds(int $minutes): int
{
return $minutes * 60;
}
echo sessionExpirySeconds(120) . PHP_EOL;
// Prints:
// 7200
Expiry should match the product's security needs. Admin areas, payment flows, and shared devices often need stricter rules than low-risk browsing sessions.
Locking And Slow Requests
PHP sessions are often locked while a request is using them. That prevents two requests from writing conflicting session data at the same time, but it can also block concurrent requests from the same user.
For example, if one request starts the session and then performs a slow export, another tab may wait before it can read or write the same session. Frameworks and session handlers vary, but the lesson is practical: do not keep session writes open longer than needed.
<?php
declare(strict_types=1);
function sessionWriteAdvice(bool $requestIsSlow): string
{
if ($requestIsSlow) {
return 'write the needed session changes early, then release the session before slow work';
}
return 'keep the session small and finish the request normally';
}
echo sessionWriteAdvice(true) . PHP_EOL;
// Prints:
// write the needed session changes early, then release the session before slow work
In plain PHP, session_write_close() can release the session lock after changes are saved. In a framework, use the framework's session lifecycle conventions.
Regenerate Session IDs After Login
After a user logs in, the session ID should be regenerated. This reduces the risk of session fixation, where an attacker tries to make a victim use a known session ID before authentication.
<?php
declare(strict_types=1);
function loginSessionActions(): array
{
return [
'regenerate_session_id' => true,
'store_user_id' => true,
'clear_old_flash_messages' => true,
];
}
print_r(loginSessionActions());
// Prints:
// [regenerate_session_id] => 1
// [store_user_id] => 1
The exact call in plain PHP is session_regenerate_id(true), after the session has started and before the authenticated state is trusted.
Cookie Security Settings
Session cookie settings matter as much as the backend. For normal HTTPS applications, the session cookie should usually be secure, HTTP-only, and same-site.
<?php
declare(strict_types=1);
function recommendedSessionCookieOptions(): array
{
return [
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
'path' => '/',
];
}
print_r(recommendedSessionCookieOptions());
// Prints:
// [secure] => 1
// [httponly] => 1
// [samesite] => Lax
Secure means the cookie is sent over HTTPS. HttpOnly prevents JavaScript from reading it. SameSite helps control cross-site request behaviour.
What Not To Store In Sessions
Avoid storing:
- passwords, access tokens, or long-lived secrets;
- full user records that can become stale;
- large arrays of product or report data;
- anything needed as a durable audit trail;
- permission decisions that should be checked fresh.
Store small identifiers and short-lived UI state. Load important data from the proper source when needed.
What To Check
Before moving on, make sure you can:
- explain that the cookie usually stores a session ID, not the whole session;
- choose file sessions for simple single-server apps and shared storage for scaled apps;
- explain why Redis is commonly used for multi-server PHP sessions;
- describe how database sessions differ from Redis sessions;
- set sensible expiry and cleanup rules;
- recognise session locking problems in slow requests;
- regenerate the session ID after login;
- protect session cookies with secure options.
Practice
Practice: Choose A Session Storage Backend
Build a small PHP helper that recommends a session storage backend and cookie settings for a PHP application.
Requirements
- Choose
filesfor a single app server. - Choose
redisfor multiple app servers when Redis is available. - Choose
databaseas the fallback when multiple app servers need shared sessions and Redis is not available. - Warn when file sessions are used with more than one app server.
- Include a TTL in seconds.
- Return secure session cookie options.
- Show one single-server example and one multi-server example.
Focus on the deployment decision. You do not need to start a real PHP session or connect to Redis.
Show solution
This solution models the backend choice separately from the framework or PHP configuration that would apply it.
<?php
declare(strict_types=1);
function chooseSessionBackend(int $appServerCount, bool $redisAvailable): string
{
if ($appServerCount === 1) {
return 'files';
}
if ($redisAvailable) {
return 'redis';
}
return 'database';
}
function sessionStoragePlan(int $appServerCount, bool $redisAvailable, int $ttlMinutes): array
{
$backend = chooseSessionBackend($appServerCount, $redisAvailable);
$warnings = [];
if ($backend === 'files' && $appServerCount > 1) {
$warnings[] = 'file sessions are local unless the session directory is shared';
}
return [
'backend' => $backend,
'ttl_seconds' => $ttlMinutes * 60,
'warnings' => $warnings,
'cookie' => [
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
'path' => '/',
],
];
}
$singleServer = sessionStoragePlan(1, false, 120);
$multiServer = sessionStoragePlan(4, true, 60);
echo $singleServer['backend'] . PHP_EOL;
echo $singleServer['ttl_seconds'] . PHP_EOL;
echo $multiServer['backend'] . PHP_EOL;
echo $multiServer['cookie']['samesite'] . PHP_EOL;
// Prints:
// files
// 7200
// redis
// Lax
The recommendation is based on reachability. A user may hit any app server on the next request, so a multi-server application needs session storage that every app server can read.