aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Web Development

Laravel Cache: A Practical Guide to Performance

A solid Laravel cache strategy is often the fastest real-world win you can give an application. Every database round-trip, every external API call and every heavy page render costs time; computing those results once and storing them instead of recalculating them on every request can drop response times to milliseconds. In this guide I cover cache drivers, query and page caching, and the most critical topic of all — invalidating the cache safely — with practical examples.

When does caching actually help?

Caching shines for data that is expensive to produce but rarely changes. Typical candidates are:

  • Reporting queries with heavy JOINs or aggregation,
  • Menus, settings or category lists shown on every page,
  • Slow or rate-limited external API responses (exchange rates, weather),
  • Full-page HTML output that changes infrequently.

By contrast, per-user data that must be instantly accurate (cart balance, payment status) is usually the wrong thing to cache. The first rule is: measure first. Cache the spot you have proven to be slow.

Choosing the right driver

Laravel's cache layer is driver-agnostic; configuration lives in config/cache.php, and the default store is set by the CACHE_STORE variable in your .env file. The main options are:

  • file — zero setup, fine for small single-server projects.
  • database — persistence without an extra service; good for moderate load.
  • redis / memcached — the recommended choice for high traffic, multiple servers and tag support.
  • array — lives only for a single request; handy in tests.

If you expect serious traffic in production, Redis is almost always the right answer. The configuration looks like this:

# .env
CACHE_STORE=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

Query caching: the remember pattern

The most used API is Cache::remember(): it returns the value if the key exists, otherwise runs the closure, stores the result and returns it. This collapses the "fetch if present, otherwise compute and store" logic into a single line.

use Illuminate\Support\Facades\Cache;

$popular = Cache::remember('projects:popular', now()->addHour(), function () {
    return Project::where('is_published', true)
        ->orderByDesc('views')
        ->take(10)
        ->get();
});

The duration can be a DateTime (now()->addHour()) or a number of seconds. If you never want it to expire, use Cache::rememberForever() — but then invalidation is entirely your responsibility.

Lower-level helpers are also available:

Cache::put('key', $value, now()->addMinutes(10));
$value = Cache::get('key', 'default');
Cache::has('key');
Cache::forget('key');
Cache::increment('visit_counter');

Full-page and fragment caching

When query caching is not enough, you can store rendered HTML too. A simple pattern at the controller level:

public function index()
{
    $html = Cache::remember('home:html', now()->addMinutes(15), function () {
        return view('site.index', $this->loadData())->render();
    });

    return response($html);
}

If you want to cache the entire response at the HTTP level, doing it in a middleware is cleaner: use the request URL as the key, check it with Cache::has(), and if it is missing, generate and store the response. On static showcase pages this approach drops database load to almost zero.

Tags and safe invalidation

The hard part of caching is not storing — it is invalidating at the right moment. When a project is updated, you must delete all related cache keys. If you use Redis or Memcached, tags make this elegant:

Cache::tags(['projects'])->put('projects:popular', $data, 3600);

// When a project changes:
Cache::tags(['projects'])->flush();

Tags are not supported on the file and database drivers; in that case delete keys manually. The most robust method is to hook into model events:

// In the Project model
protected static function booted(): void
{
    static::saved(fn () => Cache::forget('projects:popular'));
    static::deleted(fn () => Cache::forget('projects:popular'));
}

From the command line, to clear all application cache:

php artisan cache:clear

Config, route and view caches

There is a separate layer that should not be confused with application data caching: the framework's own caches. On a production server, running these after deployment yields a real speed boost:

php artisan config:cache
php artisan route:cache
php artisan view:cache
# all at once:
php artisan optimize

An important caveat: when config:cache is active, env() calls only work reliably inside configuration files. Do not call env() directly in your code — read values through config() instead. When you change configuration, rebuild it with php artisan config:clear.

Frequently Asked Questions

Why don't my changes show up while developing?

You are most likely being served a stale cache. The php artisan optimize:clear command clears the config, route, view and application caches in one go; it is the first thing to try when something looks wrong locally.

Should I use file or Redis?

For a small single-server site the file driver is more than enough. Move to Redis if you need high traffic, multiple servers, atomic counters or tag-based invalidation.

How long should the cache TTL be?

It depends on how stale the data may be. For lists that change hourly, 15–60 minutes is reasonable; for data that almost never changes, rememberForever() plus event-based invalidation is the most efficient.

Want to make performance fly with a well-designed cache? Let's measure your Laravel app's slow spots together and build the right caching strategy — get in touch with me.

Devamı için