In one sentence

A cache stores the answer to an expensive question for a while, so the next request can reuse it instead of working it out again.

📝It's the sticky note on your fridge with the pizza place's number. Looking it up online every time works, but the note is instant. You just need to update the note when the number changes.

Why it matters

Do the hard work once

⚡

Faster pages

Reading a cached value takes around a millisecond. A heavy report query can take hundreds.

🗄️

Less database load

A popular sidebar shown on every page can hit the database once an hour instead of once per visitor.

🌐

Fewer slow API calls

Exchange rates or weather from an outside API can be reused for minutes, saving time and rate limits.

The big picture

Cache hit vs cache miss

📨Requestneeds “popular posts”
🗃️Check cachekey: posts.popular
⚡Hitreturn stored value (fast)
🐢Missrun the query, store it, return it
Only the first request after the value expires pays the full cost. Everyone else gets the stored copy.
Key
The name a value is stored under, like posts.popular. Same key, same value.
TTL
“Time to live”: how long the value stays before it expires, in seconds or as a date.
Invalidation
Removing a cached value early because the underlying data changed.
Driver / store
Where the cache physically lives: database, Redis, files, memory, etc.
Step by step

Cache like a pro

  1. Pick a cache driver

    The driver is set with CACHE_STORE in .env. New Laravel 11+ apps default to database (the migration for its cache table is included).

    
              CACHE_STORE=database
            
    DriverGood forNotes
    databaseGetting started, small appsNo extra software; shared across servers
    redisProduction, high trafficVery fast in-memory store; also great for queues
    memcachedProductionFast in-memory, simpler than Redis
    fileSingle-server appsStored in storage/framework/cache; not shared between servers
    arrayTestsLives only for the current request
  2. Use Cache::remember

    This is the method you'll use 90% of the time. It returns the cached value if it exists; otherwise it runs your closure, stores the result for the TTL, and returns it.

    
              use Illuminate\Support\Facades\Cache;
    
              $popular = Cache::remember('posts.popular', now()->addMinutes(10), function () {
                  return Post::with('author')
                      ->withCount('comments')
                      ->orderByDesc('comments_count')
                      ->take(5)
                      ->get();
              });
            

    For things that rarely change, Cache::rememberForever('categories', fn () => Category::all()) keeps the value until you remove it.

  3. The basic building blocks

    remember is built on a few simpler methods you can use directly.

    
              Cache::put('site.banner', 'Sale ends Friday!', 3600);  // store for 3600 seconds
              Cache::get('site.banner', 'Welcome!');                 // read, with a default
              Cache::has('site.banner');                             // true / false
              Cache::forget('site.banner');                          // delete one key
              Cache::increment('downloads');                         // counters
            

    php artisan cache:clear empties the whole application cache.

  4. Invalidate when the data changes

    A cache that's never cleared shows stale data: a new post won't appear in “popular posts” for 10 minutes. The fix is to forget the key whenever the source data changes. Model events are a tidy place to do it.

    
              protected static function booted(): void
              {
                  $clear = fn () => Cache::forget('posts.popular');
    
                  static::saved($clear);
                  static::deleted($clear);
              }
            

    Choose keys that include anything that changes the answer, for example "user.{$user->id}.dashboard" or "posts.page.{$page}". Otherwise everyone sees the same cached result.

    ℹ️ The classic joke

    “There are only two hard things in computer science: cache invalidation and naming things.” Decide when each cached value should be cleared at the same time you write the remember call.

  5. Serve stale while refreshing with Cache::flexible

    With remember, the unlucky visitor who arrives just after expiry waits for the slow query. Cache::flexible (Laravel 11.23+) takes two times: a fresh period and a stale period.

    
              $stats = Cache::flexible('dashboard.stats', [300, 900], function () {
                  return Stats::calculate();   // slow
              });
            
    • 0–5 minutes: the value is fresh and returned as-is.
    • 5–15 minutes: the stale value is returned immediately, and Laravel recalculates it in the background after the response is sent.
    • After 15 minutes: it's recalculated like a normal miss.
  6. Group related keys with tags (Redis / Memcached)

    Tags let you clear a whole family of keys at once. They're only supported by drivers like redis and memcached, not file or database.

    
              Cache::tags(['posts'])->remember("posts.page.{$page}", 600, fn () => Post::paginate());
    
              Cache::tags(['posts'])->flush();   // clears every page at once
            
Common mistakes

Avoid this, do that

❌ Avoid

          Cache::remember('dashboard', 3600,
              fn () => auth()->user()->stats());
        

One key for every user: the first person's dashboard is shown to everyone.

✅ Do

          Cache::remember("user.{$user->id}.dashboard", 3600,
              fn () => $user->stats());
        

Include the user ID (and any filters) in the key.

❌ Avoid

          if (Cache::has('x')) {
              $x = Cache::get('x');
          } else {
              $x = slow();
              Cache::put('x', $x, 600);
          }
        

Verbose, and easy to get subtly wrong.

✅ Do

          $x = Cache::remember('x', 600, fn () => slow());
        

One line that does the same thing correctly.

💡 Pro tip: measure first

Don't cache everything “just in case”. Fix N+1 queries first (see Eager Loading), measure with Debugbar or Telescope, and cache only what's genuinely slow or heavily repeated.

🏋️ Try it yourself
  1. Cache the list of categories with Cache::rememberForever('categories', ...) and show it in your layout.
  2. Add a saved/deleted hook on Category that forgets the key. Create a category and check that it appears.
  3. Cache “popular posts” for 10 minutes and compare the query count with Debugbar on a first and second visit.
  4. Rewrite it with Cache::flexible using [300, 900].
  5. Run php artisan cache:clear and confirm the next request is a miss.
Recap

Key takeaways

Check yourself

Mini quiz