Skip to content

Laravel SDK

Automatic server-side pageviews (middleware), a facade for events. Tracking happens 100% server-side, with no tracking cookies and no JS, invisible to ad blockers. Compatible with Laravel 10 to 13, PHP ≥ 8.1.

Installation

composer require quiet-metrics/laravel-metrics

The service provider and the facade alias are registered automatically (package discovery).

Upgrading

Each 0.x minor version of the bridge requires the matching minor version of the core SDK quiet-metrics/php-metrics: upgrade both together, and only those two.

composer require quiet-metrics/laravel-metrics:^0.5 --no-update
composer update quiet-metrics/laravel-metrics quiet-metrics/php-metrics

A plain composer require fails, because the core SDK is locked in composer.lock. Composer's error message then suggests -W: that option updates every dependency of the project, Laravel included. Read the package CHANGELOG before upgrading: it flags changes in what gets counted.

Configuration

Environment variables are enough in most cases:

QUIET_METRICS_PUBLIC_KEY=qm_pub_XXXXXXXXXXXXXXXXX
QUIET_METRICS_SECRET_KEY=qm_sec_XXXXXXXXXXXXXXXX
# QUIET_METRICS_ENDPOINT=https://quietmetrics.dev/api/v1/collect

A publishable configuration file is available if needed (config/quiet-metrics.php):

php artisan vendor:publish --tag=quiet-metrics-config

Automatic pageviews

The quiet-metrics middleware sends the page view in terminate(), after the response has been sent: no impact on perceived latency. Per route or per group:

Route::middleware('quiet-metrics')->group(function () {
    Route::get('/', HomeController::class);
    // …
});

To measure all your web traffic at once (Laravel 11+):

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->appendToGroup('web', \QuietMetrics\Laravel\Middleware\TrackPageview::class);
})

Automatic pageviews are HTML/XHTML documents rendered in response to a GET, including 404/500 errors raised by one of your application's routes (abort(404), model not found). Redirects, empty responses (204/205), PDFs, JSON and attachments are excluded, along with AJAX, announced prefetches and opted-out visitors.

Addresses that match no route are not measured by the web group integration: when no route matches, Laravel runs no group middleware. These 404s include broken links and old addresses, but also most requests from bots probing websites (/wp-login.php, /.env). To measure them anyway, register the middleware in the global stack, instead of the web group:

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\QuietMetrics\Laravel\Middleware\TrackPageview::class);
})

The global stack also covers routes outside the web group, such as an admin panel: leave them out with the excluded paths in the site settings. Do not register the middleware in both places, or every page would be counted twice.

From version 0.4.0, 'track_404' => true also emits a 404 event with the page path, on the 404s the middleware sees and no others. This option is off by default: each additional event consumes quota. Enable it server-side or through the script’s data-404 on a given page to avoid duplicate events. Server tracking cannot see pages served by a cache that bypasses PHP.

Events

use QuietMetrics\Laravel\Facades\QuietMetrics;

QuietMetrics::event('signup', ['plan' => 'pro']);

From a job, a command or a listener (outside an HTTP request), there is no current URL: pass it explicitly, otherwise nothing is sent (silent drop):

QuietMetrics::event('import-finished', ['rows' => 1200], [
    'url' => 'https://mysite.com/imports',
]);

Edge cases

Full-page cache (Varnish, Cloudflare, response cache…). A page served from a cache in front of your application never runs PHP, so its visit is not counted. Keep those pages out of the cache, or add the qm.js script as well: both measurements coexist without double counting.

Octane, persistent workers. The context (URL, referrer, IP, User-Agent, language) comes from each request's Request object, never from superglobals. Nothing to configure.

Reverse proxy / CDN. The middleware reads $request->ip(): configure your Laravel application's trusted proxies as usual, and the visitor's real IP will follow. The core SDK's trust_proxy_headers option is only relevant outside Laravel.

Environments. Use a distinct site key per environment (staging vs production .env), or simply set no key in staging: without a public key, nothing is sent.

What gets sent. From version 0.5.0, the SDK only sends what Quiet Metrics reads: the page address reduced to its origin, its path and its campaign parameters, and the referrer reduced to its origin. Details in the PHP SDK.

Tests. From version 0.5.0, QuietMetrics::fake() replaces the client with a fake that records instead of sending. The middleware's pageviews go through it too.

use QuietMetrics\Laravel\Facades\QuietMetrics;

QuietMetrics::fake();

$this->post('/register', [...])->assertRedirect();

QuietMetrics::assertEventSent('signup', ['plan' => 'pro']);
QuietMetrics::assertPageviewSent();

Other assertions: assertEventNotSent('name') and assertNothingSent(). The fake records what your application asks for, before the minimization applied when sending. If your code receives the client through injection, type-hint QuietMetrics\Tracker rather than QuietMetrics\Client: that is what makes it replaceable. To keep a suite without the fake from sending anything, empty the public key in phpunit.xml (<env name="QUIET_METRICS_PUBLIC_KEY" value=""/>).

Robustness contract

Sending is non-blocking (fire-and-forget socket, ~1 ms perceived, short cURL fallback) and every failure is silent: analytics never breaks the host site, even if the platform is unreachable.

Can't find your answer?

Support replies on business days.