jeffersongoncalves/laravel-goatcounter
Laravel GoatCounter offers a simple integration with GoatCounter, the lightweight, privacy-friendly analytics platform. Configure your site code, inject the tracking script, and count page views without cookies or personal data collection.
The 1.1.0 release is an incremental, opt-in-by-environment change: scripts rendered by the package now automatically pick up Laravel's Vite CSP nonce when one is set. Nothing in your app changes unless you are running a nonce-based script-src policy, in which case this release is what makes the package compatible with it.
Day-to-day starting point:
composer update <vendor>/<package> then check vendor/<vendor>/<package>/CHANGELOG.md and skim the tag diff (git diff 1.0.x..1.1.0 in the vendor dir, or the GitHub compare view). Since there is no prior assessment on record, this release is your first documented behavioural change — capture the version you were on before upgrading.<script> (grep the package for <script and for nonce). This is the code path the release touches, and the only place behaviour can differ.config/security-headers.php (or your spatie/laravel-csp config) and confirm which middleware sets the nonce. In Laravel 10+/11 the usual path is a middleware calling Vite::useCspNonce($nonce); the package now reads that value via Vite::cspNonce().'unsafe-inline' from script-src, keep 'nonce-…', run the app, and confirm in DevTools → Elements that every <script> emitted by the package carries nonce="…" matching the response header. If the browser accepts it and the console shows no CSP violations, the upgrade did its job.If you are not using CSP at all, this release is a no-op for you; upgrade on your normal schedule.
Register the nonce before rendering. The nonce must exist on the request before any Blade view renders. Push your CSP middleware early in the web group (or globally) so Vite::useCspNonce() runs ahead of the controller:
// app/Http/Middleware/SetCspNonce.php
public function handle($request, Closure $next)
{
$nonce = base64_encode(random_bytes(16));
Vite::useCspNonce($nonce);
$request->attributes->set('csp_nonce', $nonce);
$response = $next($request);
$response->headers->set(
'Content-Security-Policy',
"script-src 'self' 'nonce-{$nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'self'"
);
return $response;
}
Register it first in bootstrap/app.php ($middleware->web(prepend: [...])) or Kernel::$middlewareGroups['web'].
Roll out in report-only mode first. Ship Content-Security-Policy-Report-Only with a report-uri, exercise the pages that use the package, and watch for violations before enforcing. This is the cheapest way to prove the nonce is actually landing on the tags the package emits in your templates, not just the ones in the README.
Verify each package entry point independently. If the package exposes more than one rendering path — a Blade component, a directive, and a facade that pushes to a stack — assert the nonce on each. The release note says "every <script>", but a custom published view that you have overridden (see Gotchas) lives outside the package's control.
Keep your own inline scripts consistent. Once a nonce is present in script-src, browsers that support CSP3 ignore 'unsafe-inline' and host allowlists. Any hand-written inline <script> in your layout must also be nonced, or it will break the moment you tighten the policy. Use @cspNonce or {{ Vite::cspNonceAttribute() }} on your own tags for symmetry.
Pair with 'strict-dynamic' if the package injects further scripts at runtime: with a nonce on the loader, dynamically created scripts inherit trust and you can drop host allowlists entirely.
Test the contract, not the implementation. A feature test that hits a representative page and asserts the header nonce appears on the package's script tag is the right regression guard:
$response = $this->get('/dashboard');
$nonce = /* extract from CSP header */;
$response->assertSee('nonce="'.$nonce.'"', false);
vendor:publish and edited the package's Blade views, Composer will not update them — your local copies will keep emitting nonce-less <script> tags while the vendor copy is fixed. Diff published views against vendor/ after every upgrade. This is the single most likely reason the fix "doesn't work" for you.responsecache, CDN/Cloudflare page rules, Cache::remember around HTML) reuse one nonce across requests, which defeats the point and gets rejected by browsers after the policy rotates. Either bypass the cache for nonce-bearing responses or move to hash-based script-src for those routes.onclick="…") and javascript: URLs are not covered by a script nonce — they need 'unsafe-hashes' or, better, removal. Same for inline style attributes, which fall under style-src, not script-src. Don't assume this release fixes a policy that was failing for style reasons.nonce="" attribute, the nonce was not set when the script rendered — check middleware ordering, an early-returning middleware, or a response built outside the HTTP stack (queue jobs, mail views, console commands, error pages rendered before middleware). In those contexts the attribute is simply omitted, which is correct.Vite::useCspNonce() / @cspNonce need a recent Laravel 10.x or 11.x. If you are pinned to an older minor, the package's call may resolve to null and silently emit no attribute. Check composer.json constraints after the update.php artisan optimize:clear after upgrading; compiled views can mask the new code path in local testing.nonce="{{ Vite::cspNonceAttribute() }}" rather than editing vendor files — it survives the next update.Refused to execute inline script. If the tag has the right nonce and it still refuses, your policy string is malformed (missing quotes around 'nonce-…', or a typo splitting the directive).How can I help you explore Laravel packages today?