Cache::forget
Removes a single item from the cache and reports whether the key was actually there.
Cache::forget(string $key): bool- Added in Laravel 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$$key | string | The cache key to remove. Only one key per call — there is no array form. |
Return value
booltrue when the item was removed, false when the key did not exist or the store failed to delete it.
Errors
Nothing for a missing key; the underlying store may throw a connection exception.
Cache::forget deletes one key. It returns true only if something was actually removed, so a false means the key was already gone — which is normally fine and occasionally a clue that you are forgetting the wrong key.
When to reach for it
Anywhere the source of truth changes and a cached derivative is now wrong: a model observer, the end of an import command, a webhook handler. Pair it with the write. Every forever and every long TTL should have a forget somewhere naming the same key, ideally produced by a shared helper so the two cannot drift.
Cache::pull($key) is read-and-delete in one atomic-ish step, which is what you want for one-shot values like a flash token.
What trips people up
The key must match exactly, prefix included. If you write with Cache::store('redis') and forget on the default store, or a queue worker runs with a different CACHE_PREFIX, nothing is removed and nothing complains.
There is no array form and no wildcard. Cache::forget('user:*') deletes a key literally named user:*. To drop a group of keys use cache tags (Cache::tags(['users'])->flush(), unsupported on the file, database and DynamoDB drivers) or put a version number in the key and bump it.
A tagged item cannot be forgotten without its tags.
Example
<?php
use Illuminate\Support\Facades\Cache;
Cache::put('user:7:profile', ['name' => 'Ada'], 3600);
// true: something was there and it is gone now.
var_dump(Cache::forget('user:7:profile'));
// false: the key was already absent. Not an error.
var_dump(Cache::forget('user:7:profile'));
// No wildcards. This removes a key literally named 'user:*'.
var_dump(Cache::forget('user:*'));
// Forget on the same store you wrote to, or nothing happens.
Cache::store('redis')->put('queue:depth', 12, 60);
var_dump(Cache::store('redis')->forget('queue:depth'));
// Read-and-delete in one call.
Cache::put('signup:token', 'abc123', 600);
echo Cache::pull('signup:token'), PHP_EOL;When to use it
- Invalidate a cached profile from a model observer when the user record is saved.
- Drop a stale aggregate at the end of a nightly import so the next request recomputes it.
- Clear a rate-limit counter after an administrator manually unblocks an account.
Frequently asked questions
Why does Cache::forget return false?
Because the key was not in the store when you asked for it removed. The usual causes are a mismatched key string, forgetting on a different store than the one written to, or a different CACHE_PREFIX in the process doing the forgetting — a queue worker that has not been restarted since the config changed is the classic case.
How do I forget multiple cache keys at once?
forget takes a single key, so loop over the keys, use Cache::tags([...])->flush() on Redis or Memcached, or embed a version number in the key and increment it. Cache::flush() removes everything on the store and ignores the cache prefix, so avoid it on a store shared with other applications.
Does Cache::forget work on tagged items?
Only if you go through the same tags: Cache::tags(['reports'])->forget('revenue'). The tags become part of the underlying key, so an untagged forget with the same name does not find the entry.
More examples
Keeping the key in one place so writes and forgets cannot drift
The bug this avoids is a key string typed twice, once as "user:{$id}:profile" and once as "users:{$id}:profile", which produces a cache that never invalidates and a forget that always returns false. One method owns the key format; the write path and both invalidation hooks call it.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class User extends Model
{
public static function profileCacheKey(int $id): string
{
return "user:{$id}:profile";
}
public static function cachedProfile(int $id): array
{
return Cache::remember(static::profileCacheKey($id), 3600, function () use ($id) {
return static::findOrFail($id)->only(['name', 'email', 'avatar_url']);
});
}
protected static function booted(): void
{
static::saved(fn (User $user) => Cache::forget(static::profileCacheKey($user->id)));
static::deleted(fn (User $user) => Cache::forget(static::profileCacheKey($user->id)));
}
}
echo User::profileCacheKey(7), PHP_EOL; // user:7:profileDropping a whole group of keys without wildcards
Neither approach uses forget on individual keys, because listing them is exactly what you cannot do. Tags cost a set membership write per entry and are unavailable on the file and database drivers. The version-prefix trick works everywhere and is O(1) to invalidate; the cost is that superseded entries linger until their own TTL expires.
<?php
use Illuminate\Support\Facades\Cache;
// Option 1: tags. Redis and Memcached only - not file, database or DynamoDB.
Cache::tags(['reports', 'team:3'])->put('revenue', 1200, 600);
Cache::tags(['reports', 'team:3'])->put('signups', 45, 600);
Cache::tags(['team:3'])->flush(); // both keys above are gone
var_dump(Cache::tags(['reports', 'team:3'])->get('revenue')); // NULL
// Option 2: a version in the key. Works on every driver.
$version = Cache::get('reports:version', 1);
Cache::put("reports:v{$version}:revenue", 1200, 600);
// "Invalidate everything" becomes a single increment.
Cache::increment('reports:version');
echo Cache::get('reports:version'), PHP_EOL; // 2
Discussion