pirsch-analytics/laravel-pirsch
Integrate Pirsch analytics into Laravel with a simple, privacy-friendly API. Track page views and custom events server-side—no cookies, no GDPR headaches—and review stats in the Pirsch dashboard.
Install and publish config
composer require pirsch-analytics/laravel-pirsch
php artisan vendor:publish --tag="pirsch-config"
Configure credentials — the package talks to the Pirsch API server-side, so you need the identifying values from your Pirsch dashboard (client ID, client secret, and the hostname you registered for the site). Put them in .env rather than committing them:
PIRSCH_CLIENT_ID=
PIRSCH_CLIENT_SECRET=
PIRSCH_HOSTNAME=example.com
PIRSCH_ENABLED=true
Version note (1.1.0): the only change in 1.1.0 is added Laravel 13 support. There are no breaking changes, no deprecations, and no new API surface — if you are on 1.0.0 the upgrade is a no-op for application code. The practical takeaway is that the package's framework constraint is now wide enough to survive an upgrade to Laravel 13 without a composer.json conflict, so you can keep it in the dependency tree while planning that jump.
First use case: track a pageview from a controller. Resolve the facade and record a hit for the current request:
use Pirsch\Facades\Pirsch;
public function show(Post $post)
{
Pirsch::track('Blog Post', [
'post_id' => $post->id,
]);
return view('posts.show', compact('post'));
}
Start with a single, low-traffic route, confirm events appear in the Pirsch dashboard, then widen out. That validates credentials, hostname matching, and the outbound network path before you wire it into hot code paths.
Middleware for automatic pageview capture. For classic server-rendered pages, a global or group middleware that fires after the response is the least invasive integration — one place to add, one place to remove, and no controller clutter. Exclude admin, health-check, and asset routes via a skip list in config rather than scattering conditionals.
Event/listener tracking for business outcomes. Pageviews are the least interesting signal. Track conversions and funnel steps from domain events (UserRegistered, OrderPlaced, SubscriptionCancelled) with a queued listener. This decouples analytics from controllers and gives you one definition per event instead of copies across the codebase.
Queue the write. The tracking call is an outbound HTTP request to a third-party API. In a request lifecycle it is pure added latency. Dispatch it from a job (ShouldQueue) or defer it with dispatchAfterResponse() so the visitor gets their response first and analytics gets written after the fact. This is the single biggest performance lever with this package.
Disable outside production. Gate tracking on config/environment so local development, CI, and test runs never hit the live API and never pollute your dashboard with fake hostnames. Keep the gate in one place (config or a service provider check) rather than in every call site.
SPA / Inertia / Livewire. Because tracking happens on the server, client-side navigations won't produce hits on their own. Either track from the components/controllers that handle those transitions, or pair server-side event tracking with Pirsch's client-side script for pageview fidelity and use the package only for the events you need to keep server-authoritative.
Metadata as a stable schema. The second argument to a track call is free-form. Treat it as a contract: define the key names once, keep them identical across every call site, and you get filterable dimensions in Pirsch. Inconsistent keys (userId vs user_id vs id) are effectively unqueryable.
The secret must stay server-side. The client secret is a write credential for your analytics account. It belongs in .env and in server-side code only. Never expose it through a Blade template, an Inertia prop, or a public JS bundle — there is no scenario where the browser needs it.
Failures should never break the app. The tracking call is a network call to a third party. Wrap it so a timeout, DNS blip, or API 5xx does not 500 a checkout page. In a queued job, let failures surface in the queue rather than silently swallowing them; in a synchronous call path, catch and log. Decide per call site which behaviour you want.
Watch for double counting. If you add server-side tracking while the Pirsch client-side script is still on the page, you will count each pageview twice. Pick one owner for pageviews and let the other source handle only what it uniquely can.
Hostname mismatch is the most common silent failure. The value you send must match the hostname registered in Pirsch. Track on a staging domain with a production hostname and the hits land in the wrong place — or get rejected. This is the first thing to check when "nothing shows up in the dashboard."
Local debugging. Set the tracking gate to disabled and, if you need to inspect payloads, bind a fake/stub implementation of the underlying client in the container for tests rather than mocking the HTTP layer ad hoc. The container binding is the clean extension point here, not method-level mocking.
Cookie banner expectations. Pirsch is cookieless and privacy-oriented, which is why it's often chosen to avoid consent flows. That is a legal determination for your jurisdiction, not a property of this package — don't let the dependency choice stand in for a compliance review. Note also that server-side tracking sends no User-Agent/Referer unless forwarded, so client hints degrade if you rely solely on the server integration.
Testing. Assert on the facade/dispatched job rather than on outbound HTTP; keep the real client unbound in the test suite so a stray config value can't post to production during a test run.
How can I help you explore Laravel packages today?