Laravel Caching Reference Guide

A Laravel caching reference guide: storing expensive-to-compute values so repeat requests are fast.

Cache stores

The store is set via CACHE_STORE in .env (this site uses database, which is also what backs its login rate limiter — a store like array only lives for a single request and would never actually throttle anything). Other options include file, redis, and memcached.

Each store makes different trade-offs. file writes serialized values to disk under storage/framework/cache — zero setup and fine for a single server, but it doesn't scale across multiple app servers since each one has its own disk, and every read/write costs a filesystem operation. database stores entries in a table (created by the cache migration) which is shared automatically across servers with no extra infrastructure, at the cost of adding load to the same database everything else queries. redis and memcached are purpose-built in-memory stores — the standard choice once an app is under real load or spread across multiple servers, and the only stores here that support cache tags. Config for each lives in config/cache.php, and different parts of an app can use different stores at once via Cache::store('redis').

Basic usage

use Illuminate\Support\Facades\Cache;

Cache::put('key', 'value', now()->addMinutes(10));

$value = Cache::get('key');
$value = Cache::get('key', 'default'); // fallback if missing
$value = Cache::get('key', fn () => 'computed default');

Cache::forever('key', 'value');

Cache::forget('key');
Cache::has('key');

Remember pattern

The pattern you'll use most: fetch from cache, or compute and store it if it isn't there yet — all in one call.

$users = Cache::remember('active-users', now()->addMinutes(30), function () {
    return User::where('active', true)->get();
});

// Never expires
$settings = Cache::rememberForever('site-settings', function () {
    return Setting::all();
});

Reach for rememberForever only for data that changes rarely and predictably, and where you have a clear plan for invalidating it — e.g. calling Cache::forget() from the code path that updates the underlying record. A permanent cache entry with no invalidation path is how sites end up serving stale data indefinitely; a short, deliberately-chosen TTL via remember() is usually the safer default even for data that "shouldn't" change often, since it puts a ceiling on how wrong a stale value can be.

Cache stampedes and locks

When a popular cache key expires, many concurrent requests can notice it's missing at the same moment and all start recomputing the same expensive value simultaneously — a "stampede" that briefly multiplies the load the cache was meant to prevent. Cache::lock() guards against this by letting only one process perform the recomputation while others wait for (or fall back past) the lock.

$lock = Cache::lock('processing-report', 10);

if ($lock->get()) {
    try {
        // Only one process reaches here at a time
        $report = generateExpensiveReport();
    } finally {
        $lock->release();
    }
}

// Block for up to 5 seconds waiting for the lock, then run the callback
Cache::lock('processing-report', 10)->block(5, function () {
    generateExpensiveReport();
});

Locks require a store that supports atomic operations — redis, memcached, database, and file all work; array does not, since it has no shared state to lock against.

Atomic increments

Cache::add('page-views', 0, now()->addDay());
Cache::increment('page-views');
Cache::decrement('stock:' . $product->id);

Tags

Group related cache entries so they can be flushed together — only supported by stores like redis or memcached, not database or file.

Cache::tags(['posts', 'author:' . $author->id])->put('post:1', $post, 600);

Cache::tags('posts')->flush();

Caching Eloquent results

Caching is orthogonal to Eloquent — there's no built-in per-model cache. Wrap the query yourself, and be deliberate about the cache key so it's invalidated (or has a short TTL) whenever the underlying data changes.

class Post extends Model
{
    public static function popular(): Collection
    {
        return Cache::remember('posts:popular', now()->addHour(), function () {
            return static::orderByDesc('views')->take(10)->get();
        });
    }
}