Palzin Track
Get 15% off with code PTRACKSIGNUP15 

Laravel Diary Logo

Guide to HTML Caching in Laravel

laravel
Table of Contents

Caching is a powerful feature that can dramatically improve the performance of your web applications. Specifically, HTML caching allows you to save the HTML output of your views, reducing the load on your server and speeding up page load times. In this comprehensive guide, we'll delve into the various ways you can implement HTML caching in a Laravel application, covering everything from the basics to more advanced topics like handling dynamic content and cache invalidation.

Before diving into the technicalities, let's first understand why HTML caching is essential. When a user visits a webpage, the server has to execute various tasks like database queries, complex calculations, and more, just to render a single view. These tasks can be resource-intensive and time-consuming, particularly for websites with high traffic.

By caching the HTML output, you reduce the need for these repetitive tasks. When a user visits a cached page, they see the saved HTML, which results in faster page load times and a better user experience. Additionally, this significantly reduces the server workload, allowing it to handle more requests efficiently.

To follow this guide, you should have:

  • A working Laravel application
  • Basic understanding of Laravel's middleware and Blade templating engine
  • Familiarity with PHP

Step 1: Creating a Cache Middleware

First, create a new middleware named CachePage.

php artisan make:middleware CachePage

Step 2: Implementing Caching Logic

Open the CachePage middleware and implement caching logic like so:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Cache;

class CachePage
{
    public function handle($request, Closure $next)
    {
        $version = config('app.cache_version', 1); // Get the current version from your config or database
        $key = 'page_' . sha1($request->url()) . "_v{$version}";

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

        $response = $next($request);
        Cache::put($key, $response->getContent(), now()->addMinutes(5));
        // Cache::forever($key, $response->getContent());
        return $response;
    }
}

Here, we generate a cache key based on the URL using SHA-1 hashing. If a cache exists for this key, we return the cached HTML. Otherwise, we continue with the request and save the HTML output in the cache for 5 minutes.

Step 3: Registering the Middleware

Register the middleware in app/Http/Kernel.php to make it available for use.

protected $routeMiddleware = [
    // ... existing middlewares
    'cache.page' => \App\Http\Middleware\CachePage::class,
];

Step 4: Applying Middleware to Routes

Now, you can apply this middleware to any route that you want to cache.

Route::get('/some-page', 'SomeController@someMethod')->middleware('cache.page');

Sometimes you might want to cache a page indefinitely until something explicitly changes. For this, you can use Laravel's forever method in the middleware.

Change the line for caching the page in the middleware to:

Cache::forever($key, $response->getContent());

This will cache the HTML output indefinitely until you manually clear it.

Step 1: Creating Blade Directives

To exclude certain sections from being cached, you can use custom Blade directives. Add these directives in your AppServiceProvider:

Blade::directive('notcached', function ($expression) {
    return "<?php ob_start(); ?>";
});

Blade::directive('endnotcached', function ($expression) {
    return "<?php ob_end_clean(); ?>";
});

Step 2: Using Blade Directives in Views

Wrap dynamic content between @notcached and @endnotcached in your Blade views.

@notcached
    <p>This is Dynamic: {{ time() }}</p>
@endnotcached

These sections will not be cached, allowing you to insert dynamic or user-specific content.

Caching is excellent, but what happens when the underlying data changes? You need to invalidate or refresh the cache. The strategies for cache invalidation can be as simple as setting an expiration time or as complex as event-based invalidation.

For example, you can invalidate cache using tags or prefixes, allowing you to clear specific types of caches.

Option 1: Cache All Web Pages Globally

To cache all routes, add your middleware to the web middleware group in RouteServiceProvider.

protected function mapWebRoutes()
{
    Route::middleware(['web', 'cache.page.forever'])
        ->namespace(this->namespace)
        ->group(base_path('routes/web.php'));
}

Option 2: Cache Specific Routes

To cache specific routes, apply middleware directly on those routes.

Route::get('/some-page', 'SomeController@someMethod')->middleware('cache.page');

Option 3: Cache Groups of Routes

To cache a group of routes, you can wrap them in a middleware group.

Route::middleware(['cache.page.forever'])->group(function () {
    Route::get('/page1', 'PageController@page1');
    Route::get('/page2', 'PageController@page2');
    // ... more routes
});

HTML caching can dramatically improve your Laravel application's performance. While it's a powerful technique, it's essential to use it judiciously, considering factors like dynamic content and cache invalidation. This guide should provide you with a solid foundation to start implementing advanced HTML caching strategies in your Laravel projects.

Bonus Content: Cache Versioning to Sync Server and Client While caching can significantly improve the performance of your application, it also introduces the complexity of keeping the cache up-to-date. One common issue is ensuring that the client-side cache is invalidated when the server-side cache is updated. Here's how you can handle this by using a cache versioning strategy.

First, create a new middleware named CachePage.

php artisan make:middleware SetCacheHeadersMiddleware

Here's how the updated middleware might look:

class SetCacheHeadersMiddleware
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        $time = 86400*7; // Cache for 7 days

        // Set versioning
        $version = 'v1';
        $response->header('ETag', $version);

        // Set caching headers for static resources
        $response->header('Expires', gmdate('D, d M Y H:i:s', time() + $time) . ' GMT');
        $response->header('Cache-Control', "public, max-age={$time}");

        return $response;
    }
}

On the client side, the browser will automatically send the ETag value back to the server in the If-None-Match header for every subsequent request to the same resource. On the server side, you can compare this against the current ETag value.

public function handle($request, Closure $next)
{
    $currentVersion = 'v1';
    $clientVersion = $request->header('If-None-Match');

    if ($currentVersion === $clientVersion) {
        return response(null, 304);  // Not Modified
    }

    // ... rest of the code
}

Whenever the content that you're caching changes, make sure to update the version number both in your server-side caching middleware and your client-side SetCacheHeadersMiddleware.

By implementing cache versioning, you can ensure that your users are always seeing the most up-to-date content, while still benefiting from the performance improvements that caching provides.

::Share it on::

Comments (0)

What are your thoughts on "Guide to HTML Caching in Laravel"?

You need to create an account to comment on this post.

Related articles