Guide to HTML Caching in Laravel
Table of Contents
- Why HTML Caching Matters
- Prerequisites
- The Basics of HTML Caching
- Caching Pages Indefinitely
- Dealing with Dynamic Content
- Invalidating the Cache
- Applying Middleware
- NOTICE!
- Bonus Content: Cache Versioning to Sync Server and Client
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.
Why HTML Caching Matters
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.
Prerequisites
To follow this guide, you should have:
- A working Laravel application
- Basic understanding of Laravel's middleware and Blade templating engine
- Familiarity with PHP
The Basics of HTML Caching
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');
Caching Pages Indefinitely
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.
Dealing with Dynamic Content
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.
Invalidating the Cache
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.
Applying Middleware
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
});
NOTICE!
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
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;
}
}
Step 3: Validate Version on Client-Side
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
}
Step 4: Update Version When Content Changes
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.
Comments (0)
What are your thoughts on "Guide to HTML Caching in Laravel"?
You need to create an account to comment on this post.