Laravel 13.33 combines memoized cache and tags
Avoid repeated Redis reads during one request without giving up tag-based cache invalidation.
When fast cache reads still add up
In a Laravel application, it is easy to fetch the same cache key more than once during a request. It can happen across services, policies, Livewire components, or layers that should not depend on each other. Redis is fast, but asking it for a value that has already been resolved is still unnecessary work. In a homelab setup, Redis may also run in another container or host, so every lookup leaves the PHP process.
Laravel already provided Cache::memo() to keep resolved cache values in memory for a single request or job execution. The first lookup goes to the configured store, while later lookups for the same key are served from memory. What was missing was a direct way to use that behavior with cache tags. Laravel 13.33 adds that combination.
A local layer in front of Redis
This does not replace Redis or add another persistent cache layer. Cache::memo() decorates the cache store you already use and keeps values only for the lifetime of the current request or job. With Redis as the underlying store, the first lookup reaches Redis and later lookups can reuse Laravel's in-memory result. Once execution ends, that in-memory cache is gone.
Tags solve a different problem: grouping related cache entries so they can be invalidated together. When a user's permissions change, I do not need to know every derived key in order to remove them one by one. I can associate them with the permissions tag and flush that group when needed. In Laravel 13.33, both concerns can be handled in the same cache call.
A permissions example
This pattern works well when the same data is needed from several places during one execution. The example loads a user's permissions, keeps them in Redis for a period of time, and prevents duplicate reads during the current request. The tag gives me a clear invalidation boundary when permissions are changed. The key still needs to identify the user, because a tag is not a replacement for a sound key strategy.
use Illuminate\Support\Facades\Cache;
public function permissionsFor(User $user)
{
$key = "permissions:{$user->id}";
return Cache::memo()
->tags(['permissions'])
->remember(
$key,
now()->addMinutes(10),
fn () => $user->permissions()->get(),
);
}When permissions change, I can invalidate the entries attached to that tag. If invalidation happens inside the same request or job, using the same memoized access also drops the in-memory copy for that execution. That avoids keeping a stale local value after changing the tagged cache.
Cache::memo()
->tags(['permissions'])
->flush();The fine print
This does not share in-memory values across requests, workers, or servers. Every HTTP request and every job gets its own memoized cache, so Redis remains the common cache layer between processes. It also does not coordinate concurrent executions on a cold key: two requests can still resolve the same value if neither finds it first. If that calculation needs protection, consider a cache lock or decide whether the extra complexity is justified.
Tags are not available on every cache driver. Laravel's documentation states that they are unsupported by the file, dynamodb, database, and storage drivers, so the actual production store matters. In a self-hosted Redis setup, the application must be configured to use the Redis cache store. Running Redis somewhere on the network is not enough if the application is still using another default driver.
Tag order matters too. Laravel requires the same ordered list of tags when retrieving an entry that was used to store it. For that reason, I would centralize tags in a method or dedicated class once the list starts growing. Cache::memo()->tags([...]) improves one access path, but it does not fix inconsistent keys or an unclear invalidation policy.
I would not add memo() everywhere by default. It is useful when the same key is requested repeatedly in a single execution and those reads target the same store. For routes that fetch a key only once, it adds little. It is a small improvement, but it removes the manual array-cache layering that this pattern previously required.