Skip to content
Devix Open Source
Laravel package v1.0.0 MIT Alpha

Laravel Cookie Consent

Consent banner middleware with a proof-of-consent log.

composer require devix-labs/laravel-cookie-consent
PHP Laravel
Laravel Cookie Consent

Live demo coming soon

The browser widget writes a first-party JSON cookie; this package reads it in Laravel. Ask Consent::allows('analytics'), wrap a tag in @consent, and it is never printed unless it is allowed — stronger than printing it and blocking it in the browser. A decision made against an older policy version stops counting the moment you bump the config. The proof-of-consent log writes one row per decision and per change of mind, with the IP stored as an HMAC rather than in the clear.

What you get

The decision, in PHP

Consent::allows('analytics'), 'analytics/ga4' or a bare service id — plus the whole decision: what was refused, how, under which regime, in which language.

@consent in Blade

Wrap the script tag. A tag that is not allowed is never sent to the browser at all.

One tag renders the widget

<x-cookie-consent /> brings your categories, your policy link, the visitor's country from your CDN header and the call that logs the decision.

Proof you own

A row per decision and per change of mind: consent id, categories, refusals, version, regime, language, notice hash, revision — and the IP as an HMAC, never in the clear.

Policy versioning that bites

Bump the version and the server stops honouring consent given to wording that no longer applies, the same moment the widget starts asking again.

The encryption trap, handled

Laravel encrypts cookies it sets; this one is written by JavaScript. The provider excepts it automatically — the usual reason a server-side consent check silently answers no.

Laravel Cookie Consent — overview

Why it exists

A consent banner in the browser can only block what the browser can see. When the decision is readable on the server, a tag that is not allowed is never printed at all — and the proof of what was agreed to lives in your database, not in a third party's dashboard.

What it does

  • Reads the decision. The widget writes a first-party JSON cookie; this package parses it, checks it against your current policy version, and answers Consent::allows('analytics').
  • Gates tags in Blade. @consent('analytics') around the script tag.
  • Renders the widget. One <x-cookie-consent /> with your categories, your policy URL and the visitor's country from your CDN header.
  • Keeps the log. One row per decision and per change of mind: consent id, categories, refusals, version, regime, language, notice hash, revision, hashed IP, user agent, and when.

Not in 1.0

A Nova/Filament screen for the log, and consent receipts by email, are deliberate omissions. The browser widget is a separate product: @devix/cookie-consent.

How it compares

Questions

How is this different from spatie/laravel-cookie-consent?

That package shows a banner and remembers that it was dismissed. It blocks nothing and there is nothing to ask on the server. This one reads an actual decision — categories, services, refusals — so you can decide in PHP whether a tag is printed, and it keeps the log.

Do I need the JavaScript widget too?

Yes — the browser is where consent is given. This package renders it for you with <x-cookie-consent /> and reads what it wrote. You can serve the widget from your own Vite build.

Why is my consent check always false?

Almost always Laravel's cookie encryption: the widget writes the cookie from JavaScript, so Laravel cannot decrypt it. This package excepts the cookie automatically — if you renamed it in the config, that exception follows the new name.

Does this work with full-page caching?

Not if the HTML varies by consent — include the decision in your cache key, or print the tags with data-cc and let the widget release them in the browser. The docs show both.

Is the log personal data?

It is written to be the least it can be: the consent id links to the visitor's own cookie, and the IP is stored as an HMAC with your APP_KEY. Trim old rows on a schedule — the docs show that too.