joostvanveen / laravel-litespeedcache
Laravel implementation of joostvanveen/litespeedcache.
Requires
- php: >=7.1
- joostvanveen/litespeedcache: ^1.0
Requires (Dev)
- orchestra/testbench: ^3.8
- phpunit/phpunit: ^7.5
README
joostvanveen/laravel-litespeedcache
A Laravel wrapper for joostvanveen/litespeedcache. If you wish to use Litespeed Cache outside of Laravel check out this framework agnostic https://github.com/joostvanveen/litespeedcache
Installation
Require the package using composer:
composer require joostvanveen/laravel-litespeedcache
Enable the Litespeed in your .htaccess file.
<IfModule LiteSpeed>
# Enable public cache
CacheEnable public /
# Enable private cache if you need to
CacheEnable private /
# Check the public cache
CacheLookup public on
# Ignore normal Cache Control headers
CacheIgnoreCacheControl On
# Maximum expiration time in seconds
CacheMaxExpire 604800
</IfModule>
Usage
This package adds a LitespeedCache
cache facade for easy use and sets middleware that automatically caches all requests, according to the config settings.
You can set which URLs (for instance any adminconsole*
) and query strings not to cache in config.
If you want to preview a page without cache, simply add ?cache_bypass=1
to a URL, or set a cache_bypass
cookie with a vale of 1.
If you want to set your own middleware, you can set the package to not use the default middleware.
Configuration
To be able to adjust the configuration for this package, publish the configuration files to your project's /config
folder like so:
php artisan vendor:publish --provider="Joostvanveen\LaravelLitespeedcache\LitespeedCacheServiceProvider" --tag=config
The config file adds the following settings to your Laravel configuration:
// Cache is enabled config(['litespeedcache.defaults.enabled' => true]); // ESI is enabled config(['litespeedcache.defaults.esiEnabled' => true]); // Whether or not to cache ajax calls config(['litespeedcache.defaults.enable_ajax_cache' => false]); // Array of request methods that should be cached config(['litespeedcache.defaults.cache_http_verbs' => ['GET', 'HEAD']]); // Whether or not to use the deafult middleware that comes with this package config(['litespeedcache.defaults.use_middleware' => true]); // Default cache type config(['litespeedcache.defaults.type' => 'public']); // Default TTL for cache in minutes config(['litespeedcache.defaults.lifetime' => 240]); // Array of URIs that should not be cached, can contain wildcards like '/admin*' config(['litespeedcache.defaults.excludedUris' => [$csrfTokenUri . '*', $csrfFieldUri . '*']]); // Array of query strings that should not be cached, can contain wildcards like '*utm_source=*' config(['litespeedcache.defaults.excludedQueryStrings' => []]); // Array of routes for this package config(['litespeedcache.routes' => ['token' => $csrfTokenUri, 'field' => $csrfFieldUri] ]);
Facade
The package registers \Joostvanveen\Litespeedcache\Cache
as a facade and sets default config values for
litespeedcache.defaults.enabled
,
litespeedcache.defaults.type
,
litespeedcache.defaults.lifetime
,
litespeedcache.defaults.excludedUris
,
and litespeedcache.defaults.excludedQueryStrings
.
use LitespeedCache; [...] // Cache use all default settings from config. LitespeedCache::cache(); // Purge the cache LitespeedCache::purge();
You can use all methods from \Joostvanveen\Litespeedcache\Cache
, see https://github.com/joostvanveen/litespeedcache/blob/master/README.md for full documentation.
Some examples:
// Full options example // You can also set $excludedUris and $excludedQueryStrings in config. $excludedUris = [ 'checkout*', 'admin*', ]; $excludedQueryStrings = [ '*direction=*', ]; LitespeedCache::setType('private')->setLifetime(120) ->addTags(['articles', 'en_GB']) ->addVary('value=mysubdomain') ->setExcludedUrls($excludedUris) ->setExcludedQueryStrings($excludedQueryStrings) ->cache(); // Purge cache using tags. LitespeedCache::addTags('articles')->purge(); // If the lifetime is set to 0 the page will not be cached (new Cache)->setEnabled(true)->setLifetime(0)->cache();
Middleware
By default, the package contains a middleware that caches all pages (except cli).
You can find this middleware at src/Middlewares/Cache.php
.
If you want to use your own middleware, you can disable the default middleware by setting the config value litespeedcache.defaults.use_middleware
to false
- Publish the config settings to your Laravel project.
php artisan vendor:publish --provider="Joostvanveen\LaravelLitespeedcache\LitespeedCacheServiceProvider" --tag=config
- Set
use_middleware
tofalse
<?php return [ 'defaults' => [ 'enabled' => true, 'use_middleware' => false, // Set Litespeed Cache Middleware to inactive 'type' => 'public', 'lifetime' => 240, ], ];
- Create a middleware class
php artisan make:middleware LitespeedCache
- Edit your middleware
<?php namespace App\Http\Middleware; use Closure; use Joostvanveen\LaravelLitespeedcache\Facades\LitespeedCache as LitespeedCacheFacade; class LitespeedCache { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * * @return mixed */ public function handle($request, Closure $next) { $excludedUris = [ 'checkout*', 'admin*', ]; LitespeedCacheFacade::setEnabled(config('litespeedcache.defaults.enabled')) ->setType(config('litespeedcache.defaults.type')) ->setLifetime(config('litespeedcache.defaults.lifetime')) ->setExcludedUrls($excludedUris) ->cache(); return $next($request); } }
Cache and csrf tokens in form requests
Laravel has built in security to guard against csrf attacks, using a csrf token. A token check is done on every POST request.
Typically, you provide the csrf token as a hidden field in a form, using csrf_field()
or in a POST ajax request,
using something like $.post(route('my.route'), $('#form').serialize() + "&_token={{ csrf_token() }});
This poses a problem when used with a full page cache like Litespeed Cache or Varnish. The csrf is unique and should not be stored in cache. If it is you will be posting a cached csrf token instead of a fresh one, resulting in a 'This page has expired' error on doing a POST request. However, you do want to store the rest of the page in cache. You need some way to do hole punching in your cached page. ESI to the rescue.
This package provides two ways to use csrf tokens in you pages as an ESI block. On constructing the cached page, the ESI block will be replaced by the actual, uncached csrf token. This way you can have a fully cached paged, but with a uncached token.
Use the following code to include a hidden field in a form on a cached page, instead of csrf_field()
. The 'litespeedcache.routes.field' route
is part of this package an will return a hidden field with csrf token.
ESI helper functions
This package comes with an easy helper functions to display an ESI block in your form.
{!! getLitespeedCsrfField() !!}
This will display the proper ESI block if ESI is enabled, and a regular crsf_field()
if ESI is not enabled.
// With esi enabled <esi:include src="{{ route('litespeedcache.csrf.field') }}" /> // With esi disabled <input type="hidden" name="_token" value="TGZUHk9BSg6QTH1knqPLPXB20EHKLmqEZSt4f3Uk">
Sometimes you need just the token, for instance for ajax POST requests. That's why there is also a special helper function that returns just the token.
{{ getLitespeedCsrfToken() }}
This will display the proper ESI block if ESI is enabled, and a regular crsf_token()
if ESI is not enabled.
// With esi enabled <esi:include src="{{ route('litespeedcache.csrf.token') }}" /> // With esi disabled TGZUHk9BSg6QTH1knqPLPXB20EHKLmqEZSt4f3Uk
Of course, you are free to manually code the ESI blocks into your forms as well.
<esi:include src="{{ route('litespeedcache.csrf.field') }}" /> <esi:include src="{{ route('litespeedcache.csrf.token') }}" />
ESI not supported by your server?
Not all Litespeed servers support ESI. If you are on a Litespeed server that does not support ESI, you can insert an uncached token into your form using ajax, since ajax requests are never cached by joostvanveen/litespeecache.
<script> $(function() { // Populate all csrf tokens by ajax using an uncached route $.get('{{ route('litespeedcache.csrf.token') }}', function(data) { $('input[name="_token"]').val(data.token); }); }); </script>
joostvanveen/litespeedcache documentation
You can find the Litespeed Cache documentation here https://github.com/joostvanveen/litespeedcache/blob/master/README.md
Litespeed documentation
You can find the Litespeed Cache documentation here https://www.litespeedtech.com/support/wiki/doku.php/litespeed_wiki:cache:developer_guide:response_headers
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.