In the web development world, speed and performance are must-haves. Whether you're building a static website, software-as-a-service (SaaS), or bespoke web software, it's important that everything loads quickly to keep your users happy. One of the most common ways to optimize for speed in Laravel is caching.

Caching refers to the practice of storing data inside a "cache" or high-speed storage layer. It's typically (but not always) stored in memory; thus, it's quicker to store and fetch data from it than from a database or file storage.

For example, let's imagine that we have some database queries that we need to perform and take a long time or use a lot of memory to run. To reduce this overhead with Laravel caching, we could perform the query and then store its results in the Laravel cache. This means that the next time we need to run this query, we can grab the results from the cache rather than execute the database query again. When the application needs to fetch the data a subsequent time, there's no need to do a costly database query.

Likewise, we can also use this approach to cache the responses of external API requests. For example, let's imagine that you need to make a request to an exchange-rate API to find the exchange rate between two currencies at a given date and time. We could cache this result after fetching it once so that we don't need to make any future requests if we need this exchange rate again. Querying a Laravel cache is much faster than making an API call, so the application spends less time fetching data.

Laravel provides a simple yet powerful API for interacting with the cache. By default, Laravel supports caching using Redis, Memcached, DynamoDB, databases, files, and even arrays.

The benefits of Laravel caching

As mentioned above, when used effectively, using a cache in Laravel can reduce the workload on your file storage system, database, and application servers. For example, let's imagine that you have a complex database query that is both CPU and memory-intensive.

Reducing expensive database queries

If this query isn't run very often, it may not be too much of an issue. However, if this query is run regularly, you might start to notice a performance hit on your application. Resource-heavy queries slow down the given query and can affect other parts of your system for other users.

So, to reduce the infrastructure overhead caused by this expensive query, we could execute it and then store the results in a cache. By doing this, the next time that we need to fetch these results, we'll be able to retrieve them from the Laravel cache instead of having to run the database query again. Since the cache lookup is much faster than almost any database query, the time saved by checking the cache first pays off even if the value doesn't always exist in the cache.

Skipping long-running scripts

We can also apply this same logic if we need to run an expensive PHP script or read a file regularly. By caching the output of the script or the contents of the file, we can reduce the application server’s overhead.

Reducing API calls

Likewise, as mentioned above, we can cache the responses from external API requests so that we don't need to make duplicate queries. By making fewer queries over the internet, we can also reduce the chances of hitting a throttle limit or monthly request limit that an API might impose. Using a Laravel cache to reduce API requests can save on both latency and API costs.

As we know, loading times are becoming more important every day, especially now that Google is using loading times as ranking factors. Research suggests that users tend to leave pages that take more than three seconds to load, so search engines tend to punish slow-loading sites in the rankings. Let's imagine that we have a database query that takes a few seconds to execute. A few seconds of latency might not seem too bad at first glance, but if you compound these few seconds with the time taken in other parts of your Laravel app to load your page, you can see how it might start to exceed the three-second threshold.

Due to how fast data can be retrieved from the Laravel cache, developers often choose to store the results of the query for at least some time so that we don't need to execute the time-intensive query again.

Different methods for caching in Laravel

Laravel provides a unified API for various caching backends, allowing developers to easily switch between different caching methods without changing their code. This flexibility enables you to choose the most appropriate caching solution for your application's needs and infrastructure.

Laravel Caching Documentation Laravel Caching Documentation

The caching method you select can significantly impact your application's performance, scalability, and ease of deployment. We'll explore using this unified API with some Laravel cache examples later in the article, but first, we'll discuss some of the options.

Using a Laravel file cache

The file cache driver prompts Laravel to store serialized, cached objects directly on the filesystem. This approach requires no additional setup or external dependencies, making it a quick choice for simple applications and commonly used for development environments.

While easy to implement, file caching is a poor choice for high-traffic applications because it won't scale well. For smaller applications or those with less frequent cache operations, the file cache may be a good choice.

Cache Laravel data with Redis

Redis is one of the most popular choices for a Laravel cache store due to its speed of cache reads. Redis stores data structures in memory, so it boasts fast read and write operations, making it ideal for high-performance caching scenarios. Laravel integrates easily with Redis, allowing you to easily integrate it into your application.

Other features like session storage can also benefit from Redis, so adding it to your application stack can have multiple uses. The benefits in terms of performance and scalability often outweigh the additional complexity of setting up and maintaining a separate server, which is why many teams use Redis for caching.

Using a database for the Laravel cache

Like other web frameworks, Laravel also supports using your existing database server for caching with a database cache driver. This method involves creating a new table in your database to store cache items. This method of using the database server as a Laravel cache is actually Laravel's default cache driver configuration.

The downside to the database cache driver is that it's generally slower than in-memory caching options like Redis and can potentially increase the load on your database server. This is useful when caching things like API calls, but not database queries, since those already go to your database server and wouldn't be sped up by caching them in the same spot.

Other options for caching Laravel data

Beyond these, Laravel supports several other caching methods. Memcached, like Redis, is a memory caching system with high performance. The array cache driver stores items in a PHP array for the current request, which is useful for testing or for data that only needs to persist during a single request.

Each of these options has its use cases and trade-offs, allowing you to choose the most appropriate caching method based on your application's specific requirements and your infrastructure constraints.

Storing data in the cache

Now that we've covered the basics of what caching is, the options you have for a cache store, and the benefits of using caching, let's explore how we can use it in our Laravel applications. For this Laravel cache example, we will be using extremely simple queries, but they should still explain the overall concepts of caching in Laravel.

Let's say that we have a method that queries the database server to fetch all the users that match a given criteria, as shown in this example:

public function getUsers($foo, $bar)
{
    return User::query()
        ->where('foo', $foo)
        ->where('bar', $bar)
        ->get();
}

Storing cache data indefinitely

If we wanted to update this method so that it caches the results of this query, we could use the forever() method, which will take the results of the user query and then store the result in the cache without an expiry date. The example below shows how we can do this:

public function getUsers($foo, $bar)
{
    $users = User::query()
        ->where('foo', $foo)
        ->where('bar', $bar)
        ->get();

    Cache::forever("user_query_results_foo_{$foo}_bar_{$bar}", $users);

    return $user;
}

A cache is typically a key-value pair store, which means that it has a unique key that corresponds to a value (the actual cached data). Therefore, whenever you try to store anything in the cache or retrieve it, you need to make sure that you are using a unique key so that you don't overwrite any other cached data.

As you can see in the example above, we have appended the $foo and $bar arguments to the end of the cache key. Doing this allows us to create a unique cache key for each parameter that might be used. This prevents the cache from returning data not associated with the intended query.

Storing cache data for a limited time

There may also be times when you want to store your data in the database but with an expiry date. This can be useful if you're caching the results of an expensive query that's used for statistics on a dashboard or a report.

So, if we wanted to cache our users query above for 10 minutes, we could use the Laravel cache put() method:

public function getUsers($foo, $bar)
{
    $users = User::query()
        ->where('foo', $foo)
        ->where('bar', $bar)
        ->get();

    Cache::put("user_query_results_foo_{$foo}_bar_{$bar}", $users, now()->addMinutes(10));

    return $users;
}

In this example, we cache the result of the users query for 10 minutes with a unique key associated with that query. Now, after 10 minutes have passed, the item will expire and won't be able to be retrieved from the cache (unless it has since been re-added).

Fetching data from the cache

Let's say that you want to update the users query from the examples above to check whether the result has already been cached. If it has, this is referred to as a cache hit and we can fetch and return it from the cache. Otherwise, we can execute the database query, cache the result for 10 minutes, and then return it. This process ensures that we first check the cache for a valid value and then proceed to make the necessary query and cache the result to gain the speed benefit for the next query.

To do this, we would use the following code:

public function getUsers($foo, $bar)
{
    $cacheKey = "user_query_results_foo_{$foo}_bar_{$bar}";

    if (Cache::has($cacheKey)) {
        return Cache::get($cacheKey);
    }

    $users = User::query()
        ->where('foo', $foo)
        ->where('bar', $bar)
        ->get();

    Cache::put($cacheKey, $users, now()->addMinutes(10));

    return $users;
}

Although this still looks readable, we can use Laravel's remember() method to further simplify the code and reduce its complexity. This method works in the same way as our example above, but abstracts out most of the work:

public function getUsers($foo, $bar)
{
    return Cache::remember("user_query_results_foo_{$foo}_bar_{$bar}", now()->addMinutes(10), function () use ($foo, $bar) {
        return User::query()
            ->where('foo', $foo)
            ->where('bar', $bar)
            ->get();
    });
}

Caching data for the lifetime of a request

There may be times when you need to run the same query multiple times in the same request. As a very basic example, let's imagine that you want to cache the user query from our examples above but only for the duration of that particular request. This way, you can still get the speed benefits and reduced resource usage of caching, without having to worry that the data will persist across other requests.

To do this, we can use the array cache driver provided by Laravel. This driver stores the data inside a PHP array in the cache, meaning that once the request has finished running, the cached data will be deleted.

To show how we could cache the user query above for the request lifetime, we could use the following:

public function getUsers($foo, $bar)
{
    return Cache::store('array')->remember("user_query_results_foo_{$foo}_bar_{$bar}", function () use ($foo, $bar) {
        return User::query()
            ->where('foo', $foo)
            ->where('bar', $bar)
            ->get();
    });
}

As you can see in the example, we used store('array') to choose the array cache driver.

This approach is particularly useful in scenarios where you have expensive API calls or database queries that you need to perform multiple times within a single request. By caching the results in memory for the duration of the request, you can significantly reduce the overall processing time and resource usage.

You don't need to worry about manually clearing the cache, as it's automatically wiped clean at the end of each request. If you need data to persist across requests, you'll need to use a different caching strategy.

Using Laravel's cache commands

Because of the way that Laravel runs, it boots up the framework and parses the routes file on each request made. This requires reading the file, parsing its contents, and then holding it in a way that your application can use and understand. So, Laravel provides a command that creates a single routes file that can be parsed much faster:

php artisan route:cache

Please note, though, that if you use this command and change your routes, you’ll need to make sure to run the following:

php artisan route:clear

This will remove the cached routes file so that your newer routes can be registered.

Similar to route caching, each time a request is made, Laravel is booted up, and each of the config files in your project is read and parsed. So, to prevent each of the files from needing to be handled, you can run the following command, which will create one cached config file:

php artisan config:cache

Like the route caching above, though, you’ll need to remember to run the following command each time you update your .env file or config files:

php artisan config:clear

Likewise, Laravel also provides two other commands that you can use to cache your views and events so that they are precompiled and ready when a request is made to your Laravel application. To cache the events and views, you can use the following commands:

php artisan event:cache
php artisan view:cache

Like all the other caching commands, though, you'll need to remember to bust these caches whenever you make any changes to your code by running the following commands:

php artisan event:clear
php artisan view:clear

Common pitfalls of caching

Although caching can have huge benefits in terms of performance, it can also lead to quite a few problems if it's implemented incorrectly.

Updating cache data

For example, if cached data is not invalidated or removed from the cache at the right time, it can lead to outdated and stale data that are incorrect. To contextualize this, let's imagine that our Laravel application has some settings stored in the database and that we fetch these settings on each page load. So, to gain a speed improvement, we decide to cache the settings for 10 minutes. If we forget to bust the cache in our code each time the settings are updated, it could lead to the settings being incorrect for up to 10 minutes, when it's automatically busted.

Using caching adds complexity

Further, it can be very easy to increase the complexity of a codebase if caching is used where it's not needed. As shown in the examples above, caching is very easy to implement in Laravel. However, it could be argued that if you don't get too much of a performance gain by caching a specific query or method, you might not need to use it. Adding too much caching in the wrong places can lead to hard-to-track-down bugs and unpredictable behavior. It's better to just add caching to the places where you get an obvious improvement.

Caching increases infrastructure overhead

Additionally, if you are adding caching to your Laravel application, it can often mean that you need to add a caching server, such as Redis, to your application's infrastructure. Using this approach could introduce an extra attack vector, as well as open an extra port on the server(s), but if your application's infrastructure security is configured well, it shouldn't be an issue. However, it should be something to remember, especially if you're caching personally identifiable information (PII), because you wouldn't want to risk any cached data being leaked as a result of an attack.

Mastering Laravel Caching

As we've explored in this article, implementing caching is a powerful and approachable strategy for uncovering significant performance benefits in Laravel applications. By optimally storing and retrieving data in a fast-to-access location like memory, you can dramatically reduce database server queries, API calls, and computational overhead, resulting in lower latency for your users.

From file-based caching to advanced solutions like a Redis cache, the flexibility of Laravel caching APIs allows you to easily switch between these methods as your application's needs evolve. While caching can significantly boost your web app's performance, it's important to use it correctly. Start by finding slow endpoints in your application and add caching strategically to these areas, being sure to properly update the cache when the underlying data changes.

Hopefully, this article has clearly demonstrated what caching is and how you can implement it within your own Laravel applications for performance gains. As you can see, caching can be added to your application really easily thanks to the API provided by Laravel.

However, it's important to remember the most common pitfalls when you add it, such as remembering to bust the cache to prevent stale or outdated data. Keep in mind that caching is just one aspect of performance optimization. Combine it with other best practices like efficient database queries, proper indexing, and code optimization to create truly high-performance Laravel applications.

Get the Honeybadger newsletter

Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo
    Ashley Allen

    Ashley is a freelance Laravel web developer who loves contributing to open-source projects and building exciting systems to help businesses succeed.

    More articles by Ashley Allen
    An advertisement for Honeybadger that reads 'Turn your logs into events.'

    "Splunk-like querying without having to sell my kidneys? nice"

    That’s a direct quote from someone who just saw Honeybadger Insights. It’s a bit like Papertrail or DataDog—but with just the good parts and a reasonable price tag.

    Best of all, Insights logging is available on our free tier as part of a comprehensive monitoring suite including error tracking, uptime monitoring, status pages, and more.

    Start logging for FREE
    Simple 5-minute setup — No credit card required