MetrikStack
Sources

Laravel

Server-side analytics for Laravel 11 and 12. A Composer package that records pageviews on your own queue after the response has been sent.

The Laravel package tags every HTML response with a request id and, after the response has been sent, puts one small event on your application's own queue. The request is never blocked and nothing is sent during it.

Requirements

  • PHP 8.2+
  • Laravel 11 or 12
  • A working queue (the sync driver works too, but then the POST happens during the request)

Installation

Not published yet. The package is not on Packagist yet, so the command below does not work today. This page describes how it will work. Until then the browser script and the edge and log sources cover the same traffic.

composer require metrikstack/laravel

The service provider is auto-discovered. Publish the config if you want to edit it:

php artisan vendor:publish --tag=metrikstack-config

Set the environment variables:

METRIKSTACK_TOKEN=your-site-token
METRIKSTACK_ENDPOINT=https://api.metrikstack.com
# optional
METRIKSTACK_ENABLED=true
METRIKSTACK_QUEUE_CONNECTION=redis
METRIKSTACK_QUEUE=metrikstack
METRIKSTACK_TRUST_PROXY="*"

MetrikStack looks up the site from the token, so you don't configure a site id.

Middleware

The provider adds MetrikStack\Laravel\Middleware\TrackPageview to the web middleware group, so tracking starts after composer require.

To register it yourself instead, drop it from the group and add it explicitly in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        \MetrikStack\Laravel\Middleware\TrackPageview::class,
    ]);
})

Only requests that satisfy all of the following are tracked:

  • method GET or HEAD
  • Accept header contains text/html
  • the path is not an asset: not ending in .js .css .png .jpg .jpeg .gif .svg .ico .woff .woff2 .ttf .map .json .xml .txt, and not under /_debugbar, /storage, /vendor, /build, /assets, /static

Both lists are configurable via ignore_extensions and ignore_paths.

Blade

Add the meta tag to your layout's <head>:

<head>
    <meta charset="utf-8">
    @metrikstackMeta
    ...
</head>

It renders <meta name="metrikstack-request-id" content="...">, and nothing on pages that aren't tracked. The id is also shared with every view as $metrikstackRequestId if you'd rather render it yourself.

Custom events

use MetrikStack\Laravel\Facades\MetrikStack;

MetrikStack::track('signup', ['plan' => 'pro']);

Custom events are queued the same way and, when raised during a request, carry that request's id so they line up with the pageview.

Why the request id matters

A pageview can be seen by more than one source: this package on the server and the browser script in the page. Both send the same request_id, so they are merged into one pageview (see How merging works). The id reaches the browser in two ways, so it gets through most setups:

  • <meta name="metrikstack-request-id">, from @metrikstackMeta
  • Server-Timing: metrikstack;desc=<id>, read by the script via performance.getEntriesByType('navigation')[0].serverTiming, which works through CDNs where the meta tag might be cached away

The response also carries X-MetrikStack-Request-Id for debugging and log correlation.

What is sent

These fields and nothing else:

FieldSource
namepageview, or your custom event name
urlfull URL, query string stripped to utm_* and ref only
referrerReferer header
request_idUUIDv7 generated per HTML response
tsRFC 3339 timestamp
status_coderesponse status
methodGET / HEAD
ip$request->ip(), hashed and discarded at ingest
user_agentUser-Agent header
accept_languageAccept-Language header

Never sent: cookies, session data, form/request bodies, headers beyond the three above, route parameters, authenticated user identity, or the full query string.

IP and proxies

ip is whatever $request->ip() returns. Behind a load balancer set METRIKSTACK_TRUST_PROXY to * (trust the immediate peer) or to a comma-separated list of proxy IPs/CIDRs. Leave it unset to use the application's own TrustProxies configuration untouched. The IP is hashed at ingest and the raw value discarded.

Delivery

MetrikStack\Laravel\Jobs\SendEvents POSTs {"events": [...]} to {endpoint}/v1/batch with Authorization: Bearer <token>, a 5 s HTTP timeout, 2 tries and a [5, 30] second backoff. After that the batch is dropped, so a MetrikStack outage can't clog your queue. Nothing is sent when enabled is false or the token is empty. The request id headers are still set, so the browser script keeps working.

On this page