richan-fongdasen/laravel-varnishable
Installation
composer require richan-fongdasen/laravel-varnishable
Publish the config file:
php artisan vendor:publish --provider="RichanFongdasen\Varnishable\VarnishableServiceProvider" --tag="config"
Configure Varnish
Edit config/varnishable.php:
'host' => '127.0.0.1',
'port' => 6081,
'timeout' => 5.0,
First Use Case: Cache a Route
Add the Varnishable middleware to a route or group:
Route::get('/cached-page', function () {
return response()->json(['data' => 'Cached via Varnish']);
})->middleware('varnishable');
app/Http/Middleware/Varnishable.php (default behavior).config/varnishable.php (adjust Varnish server settings).Varnishable::purge() (for manual cache invalidation).Use the varnishable middleware on routes that should be cached:
Route::get('/products/{id}', [ProductController::class, 'show'])
->middleware('varnishable');
Override the default cache key logic (e.g., for API responses):
use RichanFongdasen\Varnishable\Traits\Varnishable;
class ProductController extends Controller
{
use Varnishable;
public function show($id)
{
$response = response()->json(['product' => Product::find($id)]);
return $this->varnishable($response, "product_{$id}");
}
}
Purge Varnish cache programmatically (e.g., after updates):
use RichanFongdasen\Varnishable\Facades\Varnishable;
public function update(Product $product)
{
$product->update($request->all());
Varnishable::purge("product_{$product->id}");
}
Skip Varnish for non-GET requests or authenticated users:
Route::middleware(['varnishable', 'throttle:60'])->group(function () {
Route::get('/public-data', [DataController::class, 'index']);
});
Cache WebSocket events (if Varnish supports X-Push-Channel headers):
Route::get('/broadcasting/auth', function () {
return response()->json(['data' => 'cached-auth']);
})->middleware('varnishable');
Varnish Server Mismatch
host/port in config/varnishable.php match your Varnish instance.telnet 127.0.0.1 6081
Cache Key Collisions
$this->varnishable($response, "custom_key_{$id}");
Non-200 Responses
2xx/3xx responses. Handle errors gracefully:
if ($response->isClientError()) {
return $response; // Skip caching
}
TTL Conflicts
s=3600) may override Laravel’s cache. Set explicit headers:
$response->header('Cache-Control', 'public, max-age=300');
HTTPS/SSL Issues
X-Forwarded-Proto headers are trusted:
TrustedProxy::setTrustedProxies(['127.0.0.1']);
Check Varnish Logs
tail -f /var/log/varnish/varnish.log
Test Locally
Use varnishd -n test -f /etc/varnish/default.vcl -s malloc,100M for isolated testing.
Disable Caching Temporarily
Set VARNISHABLE_ENABLED=false in .env to bypass middleware.
Monitor Cache Hits/Misses Add Varnish stats to your dashboard:
curl http://127.0.0.1:6082/?show=vary
Custom Middleware
Extend VarnishableMiddleware to add logic:
class CustomVarnishable extends VarnishableMiddleware
{
protected function shouldCache(Request $request)
{
return parent::shouldCache($request) && !$request->has('nocache');
}
}
VCL Snippets Inject custom Varnish Configuration Language (VCL) via config:
'vcl_snippet' => '@@ include "custom.vcl";',
Event Listeners
Hook into varnishable.purged events:
Varnishable::purge('key')->then(function () {
Log::info('Varnish cache purged');
});
Queue Purging Offload purges to a queue for high-traffic sites:
Queue::push(new PurgeVarnishJob('key'));
How can I help you explore Laravel packages today?