Store temporary verification tokens, OTPs, and one-time codes in Laravel's cache with a defined TTL instead of adding temporary columns to database tables.
When implementing email verification codes, SMS OTPs, or passwordless login tokens, a common instinct is adding temporary columns (such as verification_code and verification_expires_at) to the users table.
For data that is temporary by design, adding columns to permanent database tables creates schema clutter and requires recurring cleanup queries. Laravel's cache provides a cleaner mechanism for short-lived state with automatic expiration.
Storing and Verifying Temporary Tokens
You can store the token with a defined expiration time using Cache::put(), retrieve it upon verification, and consume it immediately with Cache::forget():
use Illuminate\Support\Facades\Cache;
// Temporarily store the token for 10 minutes.
Cache::put(
'verify:'.$user->id,
$token,
now()->addMinutes(10),
);
// Retrieve the temporary token.
$cached = Cache::get('verify:'.$user->id);
if (is_null($cached)) {
// Expired
} elseif (hash_equals($cached, $request->token)) {
Cache::forget('verify:'.$user->id);
// Verified
} else {
// Invalid token
}
How It Works
- Self-managing lifecycle: When you pass a
DateTimeor integer duration toCache::put(), the cache store automatically discards expired values without requiring scheduled cleanup commands. - Timing-safe comparison: Using
hash_equals()mitigates timing attacks when comparing sensitive tokens compared to standard equality checks. - Immediate invalidation: Calling
Cache::forget()immediately consumes the token once verified, preventing replay attempts within the remaining TTL window.
When Cache Fits Best
Using cache is a practical choice for genuinely ephemeral data:
- Short-lived OTPs or SMS confirmation pins (e.g. 5 to 10 minutes).
- Email change confirmation tokens.
- Temporary rate limiting counters and challenge nonces.
When a Database Table Is Better
Cache is not a universal substitute for persistent storage. You should continue using database tables when the data requires:
- Durable auditing: Preserving an immutable historical record of every issued and redeemed token for compliance.
- Complex queries: Searching or filtering records across multiple dimensions (such as finding all pending tokens issued by an admin).
- Guaranteed persistence: Certain cache drivers (like Memcached or Redis under memory pressure) may evict keys before their TTL expires if memory limits are exceeded.