Proxy guides
Laravel
Serve the ProductLense script from your Laravel app and forward events with the HTTP client, excluded from CSRF checks, on the Scale plan.
Two routes in routes/web.php serve the script from your domain and, on the Scale plan, forward events. Read the proxy overview first for what each one fixes.
The routes
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;
Route::get('/js/a.js', function () {
$js = Cache::remember('productlense-a-js', 3600, function () {
$r = Http::timeout(5)->get('https://api.productlense.app/a.js');
return $r->successful() ? $r->body() : null;
});
abort_if($js === null, 502);
return response($js, 200, [
'Content-Type' => 'application/javascript; charset=utf-8',
'Cache-Control' => 'public, max-age=86400',
]);
});
Route::post('/m/api/e', function (Request $request) {
$raw = $request->getContent();
abort_if(strlen($raw) > 8192, 413);
$body = json_decode($raw, true);
abort_unless(is_array($body), 400);
$extra = ['ip' => $request->ip(), 'ua' => (string) $request->userAgent()];
if (isset($body['b']) && is_array($body['b'])) {
$body['b'] = array_map(fn ($e) => is_array($e) ? array_merge($e, $extra) : $e, $body['b']);
} else {
$body = array_merge($body, $extra);
}
$r = Http::timeout(5)
->withToken(config('services.productlense.key'))
->post('https://api.productlense.app/api/v1/events', $body);
return response()->noContent($r->status());
})->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]);'productlense' => [
'key' => env('PL_API_KEY'),
],The events route is excluded from CSRF verification because the browser's beacon carries no token. $request->ip() is the visitor's address once TrustProxies is configured for your load balancer.
Use it
Serve only the script on any plan:
<script defer data-site="SITE_ID" data-api="https://api.productlense.app" src="/js/a.js"></script>On the Scale plan, set PL_API_KEY in .env to a key with the events scope and change the tag to data-api="{{ url('/m') }}".
Check it works
- Run
php artisan route:list --path=m/apito see the route, then openhttps://example.com/js/a.js: it returns JavaScript. - Load a page with the developer tools open. The Network tab shows a
POSTtoapi/ewith status204. - Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.