jeffersongoncalves/laravel-plausible
A Laravel package for integrating Plausible Analytics into your PHP application. It provides simple configuration and a clean API for adding privacy-friendly, cookie-free analytics and tracking events from your Laravel projects.
This package renders <script> tags into your Laravel responses (embeds, widgets, analytics snippets). As of v3.1.0, each of those tags automatically inherits Laravel's Vite CSP nonce, so you no longer need 'unsafe-inline' in your script-src policy.
Minimal path to a working setup:
Install and publish config as usual (composer require, then php artisan vendor:publish if the package ships config/views).
Confirm how your app sets a nonce. There are two common sources:
Vite::useCspNonce($nonce) early in the request (typically middleware).laravel-security-headers) that generates the nonce and emits the Content-Security-Policy header.The package reads whichever nonce Vite has registered — it does not generate one itself.
Render your first script via the package's normal API (Blade directive/component or facade call) and inspect the HTML output. You should see nonce="..." on the tag.
Verify against the header. The nonce on the tag must be byte-identical to the one in script-src 'nonce-...'. If the tag has a nonce but the browser still blocks it, the mismatch is almost always two different nonce sources.
First real use case: dropping a third-party embed or analytics snippet into a Blade layout while keeping a strict script-src 'nonce-…' 'strict-dynamic' policy.
Set the nonce once, early, and let everything inherit it. A dedicated middleware is the cleanest place:
// app/Http/Middleware/SetCspNonce.php
public function handle($request, Closure $next)
{
Vite::useCspNonce(base64_encode(random_bytes(16)));
return $next($request);
}
Register it globally (or on every route group that renders pages with scripts) and make sure it runs before the response is rendered, not in terminate().
Pair with a headers package. If you use laravel-security-headers, let it own the header and the nonce generation, and configure your middleware to reuse that same value rather than minting a second one. One nonce per response — always.
Layout-level integration. Because the package now emits the nonce itself, you can delete any hand-rolled nonce="{{ ... }}" hacks in your Blade templates. Leave the package's rendering calls alone; the attribute appears automatically.
Cache/partial patterns. If you render script tags inside a fragment you cache (@cache, response cache, full-page cache), the nonce gets frozen into the cached HTML. Two options: exclude those fragments from caching, or render the nonce-bearing tag outside the cached region.
Feature-flag during migration. Roll out by tightening CSP in report-only mode first (Content-Security-Policy-Report-Only), watching violation reports for your package-rendered scripts, then enforce. This isolates package-related breakage from the rest of your inline-script debt.
Tests. Assert the nonce propagates without asserting a specific value:
$response->assertSee('nonce="', false);
// or, tighter: compare the tag's nonce to Vite::cspNonce() inside the test
Vite::useCspNonce() runs after the view is rendered (e.g. in terminate(), or a middleware ordered after the response is built), tags render without a nonce. Check php artisan middleware:list-style ordering / $middlewarePriority.<script> only. Inline style="..." attributes, onclick="..." handlers, and javascript: URLs still need style-src/script-src-attr treatment. Don't assume the policy is now fully strict.<script> elements, copy the nonce explicitly (e.g. from document.currentScript?.nonce or a meta tag). The package can only annotate what it renders server-side.{{ }} for the attribute value / an appropriate raw output path — a double-escaped nonce silently fails to match.'strict-dynamic' changes the picture. With strict-dynamic, nonce-bearing scripts can load further scripts, and host allowlists are ignored in CSP3 browsers. Test in the browsers you actually support; older Safari behaves differently.nonce attributes — remove your patch. Anything asserting on exact tag markup in tests may need updating.Content-Security-Policy header on the same response (identical?), check the console message (mismatch vs. missing vs. strict-dynamic interaction), then check for caching layers in between.How can I help you explore Laravel packages today?