Laravel’s Hidden Safety Net: What Happens When Cache TTL is Zero
While working on caching implementation for an API client, I discovered an interesting Laravel behavior that isn’t widely documented: what happens when you try to cache data with a TTL of zero or less?
The Discovery
I had code that looked like this:
$ttl = $this->authSession->getRemainingTtl(); // Could be 0 if expired
$this->cache->put($cacheKey, $result, $ttl);
My concern was: what if getRemainingTtl() returns 0 because the auth session expired? Would Laravel:
- Store the data anyway (bad – invalid cache)
- Throw an exception (annoying but safe)
- Silently ignore it (confusing)
- Do something smarter?
The Answer: Laravel Deletes The Key
Diving into vendor/laravel/framework/src/Illuminate/Cache/Repository.php, lines 197-220:
public function put($key, $value, $ttl = null)
{
if (is_array($key)) {
return $this->putMany($key, $value);
}
if ($ttl === null) {
return $this->forever($key, $value);
}
$seconds = $this->getSeconds($ttl);
if ($seconds <= 0) {
return $this->forget($key); // ← The magic line
}
$result = $this->store->put($this->itemKey($key), $value, $seconds);
if ($result) {
$this->event(new KeyWritten($key, $value, $seconds));
}
return $result;
}
When TTL ≤ 0, Laravel calls forget($key) – it actively deletes the cache entry.
Why This Is Brilliant
This behavior prevents a class of bugs:
- If the key doesn’t exist yet → nothing happens (safe, no-op)
- If the key already exists → it gets deleted (removes stale data)
- No invalid cache entries with expired TTL ever get stored
This means you don’t need defensive code like:
// ❌ Unnecessary guard
if ($ttl > 0) {
$this->cache->put($cacheKey, $result, $ttl);
}
You can safely do:
// ✅ Laravel handles it gracefully
$this->cache->put($cacheKey, $result, $this->authSession->getRemainingTtl());
Practical Applications
This is particularly useful when caching API responses with dynamic TTLs tied to auth tokens:
public function fetchData(string $endpoint): array
{
$cacheKey = "api:{$endpoint}";
$cached = $this->cache->get($cacheKey);
if ($cached) {
return $cached;
}
$this->ensureAuthenticated();
$data = $this->request('GET', $endpoint);
// Cache expires when auth token expires
// If token is already expired (TTL = 0), Laravel auto-deletes the key
$this->cache->put(
$cacheKey,
$data,
$this->authToken->getExpiresIn()
);
return $data;
}
The Takeaway
Laravel’s cache implementation has thoughtful edge-case handling. When you pass TTL ≤ 0 to put(), it doesn’t fail or store invalid data – it actively cleans up by deleting the key. This makes caching with dynamic TTLs safer and requires less defensive code.
Small framework details like this add up to more robust applications.