Backend frameworks · 5 min setup
How to add analytics to a Laravel app
<head> of your main Blade layout (for example resources/views/layouts/app.blade.php, or app.blade.php with Inertia), wrapped in @production. Livewire wire:navigate and Inertia page visits are counted as pageviews automatically.Updated
The browser tag covers traffic. Laravel apps usually also want two server-side pieces: signups recorded where the account is actually created, and revenue tied to the visit that earned it. Both use the visitor id the tracker stores in a first-party _vt_vid cookie — which Laravel can read once you exclude it from cookie encryption.
Laravel at a glance
- Where the tag goes
- Main Blade layout
<head>(layouts/app.blade.php/app.blade.php) - Production only
@production … @endproduction- SPA-style navigation
- Livewire
wire:navigateand Inertia tracked - Server events
Http::withToken()->post('/api/track')- Revenue
- Cashier
checkout()withclient_reference_id
How to install VisitTrack on Laravel
Step 1: Add your keys to config/services.php
Put the site id (public) and a secret API key from VisitTrack's Settings → API / MCP in
.env, and expose them through config.config/services.php// config/services.php 'visitrack' => [ 'site_id' => env('VISITRACK_SITE_ID'), 'key' => env('VISITRACK_API_KEY'), // server-side only ],Step 2: Add the tag to your Blade layout
In the layout every page extends, inside
<head>.@productionrenders it only whenAPP_ENV=production.resources/views/layouts/app.blade.php<head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> @vite(['resources/css/app.css', 'resources/js/app.js']) @production <script defer data-site="{{ config('services.visitrack.site_id') }}" src="https://visitrack.app/tracker.js"></script> @endproduction </head>Step 3: Let Laravel read the visitor cookie
Laravel encrypts cookies and ignores ones it can't decrypt. The tracker's
_vt_vidcookie is written by the browser, so exclude it from encryption (Laravel 11+ shown; on Laravel 10, add it to$exceptinApp\Http\Middleware\EncryptCookies).bootstrap/app.php// bootstrap/app.php ->withMiddleware(function (Middleware $middleware) { $middleware->encryptCookies(except: ['_vt_vid']); })Step 4: Deploy and browse
Deploy with
APP_ENV=production, open the live site and click around. Localphp artisan servetraffic is excluded twice over: by@productionand by the tracker's localhost check.
How to check VisitTrack is working on Laravel
- View source on the live site — the tag is in
<head>with your site id. - Browse two pages; VisitTrack's live view shows you and the Pages tab lists both routes.
- In DevTools → Application → Cookies, you'll see
_vt_vidon your domain (standard mode). That's the id your controllers can now read.
Does VisitTrack track Laravel page navigation?
Classic Blade apps do full page loads — one pageview each. With Livewire's wire:navigate or Inertia, pages change without a reload and the URL is updated through the History API, which the tracker follows. Livewire runs scripts in <head> only on the first visit and keeps them across navigations, so the tag isn't executed twice.
Keep logged-in areas out of your marketing numbers with data-exclude="/dashboard*,/admin*" on the tag, or use a separate VisitTrack site for the app.
How to track custom events and goals in Laravel
Record signups on the server, where you know the account exists. Breeze and Jetstream fire Illuminate\Auth\Events\Registered after creating a user, so a listener is the cleanest hook:
// app/Listeners/SendSignupToVisitTrack.php
namespace App\Listeners;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Http;
class SendSignupToVisitTrack
{
public function handle(Registered $event): void
{
$visitorId = request()->cookie('_vt_vid');
if (! $visitorId) return; // cookieless mode or blocked tracker
Http::withToken(config('services.visitrack.key'))
->timeout(3)
->post('https://visitrack.app/api/track', [
'type' => 'event',
'visitorId' => $visitorId,
'name' => 'signup',
'props' => ['method' => 'email'],
]);
Http::withToken(config('services.visitrack.key'))
->timeout(3)
->post('https://visitrack.app/api/track', [
'type' => 'identify',
'visitorId' => $visitorId,
'externalId' => (string) $event->user->id,
'traits' => ['email' => $event->user->email, 'plan' => 'free'],
]);
}
}Laravel 11+ discovers listeners in app/Listeners automatically. An event named signup fills the Signups tab; identify puts the user in People. Prefer a queued listener (implements ShouldQueue) — but then capture the cookie value in the controller first, since a queued job has no request. Browser-side, data-vt-goal="…" and window.visitrack() work in Blade and Livewire views as usual. Reference: server-side events.
Revenue attribution on Laravel
Laravel Cashier (Stripe) creates Stripe Checkout Sessions, and its checkout() methods accept session options — so passing the visitor id as client_reference_id is one line. Connect Stripe in VisitTrack's Settings → Revenue and every payment is credited to the referrer, campaign and landing page that first brought that visitor. See Stripe revenue attribution.
// One-off payment
return $request->user()->checkout(['price_xxx' => 1], [
'success_url' => route('checkout.success'),
'cancel_url' => route('pricing'),
'client_reference_id' => $request->cookie('_vt_vid'),
]);
// Subscription
return $request->user()
->newSubscription('default', 'price_monthly')
->checkout([
'success_url' => route('dashboard'),
'cancel_url' => route('pricing'),
'client_reference_id' => $request->cookie('_vt_vid'),
]);Track AI crawlers in Laravel
GPTBot, ClaudeBot and PerplexityBot don't run JavaScript, so the browser tag never sees them. A terminable middleware reports each request after the response has been sent, so crawlers never wait. Keep in mind that files in public/ (like robots.txt) are served by your web server without reaching Laravel — serve robots.txt from a route if you want those requests counted. Background: AI crawler tracking.
// app/Http/Middleware/ReportCrawlers.php — append it in bootstrap/app.php
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
public function terminate(Request $request, Response $response): void
{
try {
Http::timeout(2)->post('https://visitrack.app/api/collect-bot', [
'siteId' => config('services.visitrack.site_id'),
'path' => '/' . ltrim($request->path(), '/'),
'userAgent' => $request->userAgent(),
'ip' => $request->ip(),
]);
} catch (\Throwable) {
// never let analytics break a request
}
}Script options you might need on Laravel
All optional — add them to the same tag. Full reference: script configuration.
| Attribute | What it does |
|---|---|
data-cookieless | Store nothing in the browser; the server derives a daily-rotating id. See cookieless mode. |
data-exclude="/app/*" | Never track these paths (comma-separated, * wildcard). Checked on every navigation. |
data-allow-local | Track on localhost and *.local — for testing an install only. |
data-debug | Log every event the script sends, and why it skipped one, to the console. |
data-auto="false" | Turn off automatic events (downloads, contact clicks, form submits, rage clicks, JS errors, 404s). |
Laravel troubleshooting
$request->cookie('_vt_vid') is always null
The cookie is still being decrypted. Exclude _vt_vid from EncryptCookies as in step 3. In cookieless mode there is no cookie at all — use your own user id as visitorId, as described in cookieless mode.
The tag doesn't render on the server
@production only renders when APP_ENV=production. Check your environment, and clear cached views with php artisan view:clear.
/api/track answers 401
The API key is missing or wrong. Create a key in Settings → API / MCP and check config('services.visitrack.key') isn't cached empty (php artisan config:clear).
Signups counted twice
You're sending signup from the listener and from the browser. Report each signup from one place only.
CSP blocks the tracker
If you use a CSP package such as spatie/laravel-csp, allow the origin: script-src 'self' https://visitrack.app; connect-src 'self' https://visitrack.app;
Laravel analytics FAQ
Where do I add an analytics script in Laravel?
In the <head> of the Blade layout every page extends — resources/views/layouts/app.blade.php in Breeze/Jetstream, resources/views/app.blade.php with Inertia. Wrap it in @production so local and staging environments don't send data.
Does VisitTrack work with Livewire wire:navigate and Inertia?
Yes. Both update the URL through the History API, and the tracker counts each pathname change as a pageview. Head scripts aren't re-run on navigation, so nothing is double-counted.
How do I track Laravel signups server-side?
Listen for the Registered event and POST a signup event to /api/track with a secret API key, using the _vt_vid cookie as visitorId. See server-side events.
Can VisitTrack attribute Laravel Cashier revenue?
Yes, for Stripe Checkout: pass client_reference_id with the visitor id in Cashier's checkout() options and connect Stripe in Settings → Revenue. Paddle (Cashier Paddle) is supported too via custom_data — see Paddle.
Why is the _vt_vid cookie unreadable in Laravel?
Laravel's EncryptCookies middleware tries to decrypt every cookie and drops ones it can't. Add _vt_vid to its except list so the plain value reaches your controllers.
Is VisitTrack GDPR-friendly for a Laravel SaaS?
In cookieless mode nothing is stored on the device and IPs are never stored. Server-side signup attribution then uses your own user id. General information, not legal advice.
Keep going
- Django analyticsAdd VisitTrack to Django in base.html via a context processor, allow it in Django 6's built-in CSP, and send signups and Stripe revenue from views.
- Ruby on Rails analyticsAdd VisitTrack to Rails in application.html.erb — Turbo Drive visits tracked — with CSP initializer settings, Devise signups and Stripe Checkout revenue.
- Next.js analyticsAdd VisitTrack to a Next.js App Router or Pages Router app with next/script — client-side route changes are tracked automatically.
- Cloudflare Pages analyticsAdd VisitTrack on Cloudflare Pages in your HTML or with an HTMLRewriter middleware, exclude it from Rocket Loader, and track AI crawlers at the edge.
- Install the tracking scriptThe reference tag, CSP notes and how to check it's working.
- Custom events and signupsvisitrack(), data-vt-goal, scroll events and server-side signups.
- Use casesHow SaaS teams, indie hackers, stores and agencies use VisitTrack.
- VisitTrack vs Google AnalyticsCookie-free analytics with revenue attribution, compared honestly.
Add VisitTrack to your Laravel site
Cookie-free analytics with revenue attribution, live visitors, funnels and session replays. One script tag, every feature on every plan. 14 days free, no card required.
Not on Laravel? See all 29 integration guides.