MenuLaravel

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

routes/web.php
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]);
config/services.php
'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:

HTML
<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

  1. Run php artisan route:list --path=m/api to see the route, then open https://example.com/js/a.js: it returns JavaScript.
  2. Load a page with the developer tools open. The Network tab shows a POST to api/e with status 204.
  3. Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.

    ↑ ↓ to move, Enter to open