GDPR & Cookie Consent

A complete GDPR compliance toolkit for your Larapen site. Display a customizable cookie consent banner, log visitor consent decisions for audit compliance, and manage cookie categories from the admin panel.

Cookie Consent Banner

A polished, responsive banner with Accept All, Reject All, and Customize options. Seven position variants including modal mode.

Granular Categories

Four cookie categories (Essential, Analytics, Marketing, Preferences) with per-category toggle controls for visitors.

Consent Logging

Full audit trail of every consent decision: IP address, user agent, action taken, and categories selected. GDPR Article 7 compliant.

Auto-injection

The banner is automatically injected into all front-end pages via middleware. No template changes needed. Skips admin panel and AJAX requests.

Use Cases

EU/EEA Website Compliance

Your site serves visitors in the European Union and must comply with the GDPR and ePrivacy Directive.

  • Enable the cookie consent banner to inform visitors about cookie usage.
  • Enable consent logging to maintain an auditable record of all consent decisions.
  • Link your privacy policy page so visitors can review your data practices.
  • Configure category toggles so visitors can opt in/out of analytics and marketing cookies.

Analytics-Heavy Site

You use Google Analytics, Hotjar, or similar tools and need user consent before loading tracking scripts.

  • Enable the Analytics category toggle.
  • Read the consent cookie value (gdpr_consent) in your JavaScript before loading analytics scripts.
  • Only load analytics when categories.analytics === true.

Marketing & Ad Retargeting

You run Facebook Pixel, Google Ads, or email marketing tools that set cookies.

  • Enable the Marketing category toggle.
  • Gate all marketing/advertising scripts behind the marketing consent flag.
  • Visitors can revoke consent at any time via the withdraw endpoint.

Requirements

  • Larapen CMS v1.0.0 or later
  • PHP 8.3+
  • MySQL 8.0+
Note: This add-on is fully standalone: it has no dependencies on other add-ons. It works out of the box once activated.

Installation

Step 1: Place the Add-on

Copy or symlink the gdpr folder into your Larapen "extensions/addons" directory:

Step 2: Activate the Add-on

Go to Admin → Add-ons → Installed Add-ons and activate GDPR & Cookie Consent.

Step 3: Run Migrations

This creates the gdpr_consent_logs table for storing consent audit records.

Step 4: Set Permissions

The add-on registers 4 permissions (see Permissions). Assign them to admin roles via Admin → Users → Roles & Permissions.

Step 5: Configure

Navigate to Admin → GDPR → Settings to configure the banner position, cookie lifetime, consent logging, and cookie categories. See Configuration for details.

That’s it! The cookie banner will automatically appear on all front-end pages for visitors who haven’t made a consent choice yet. No template modifications required.

Configuration

All settings are managed in Admin → GDPR → Settings (stored in the settings table, group gdpr). Default values come from config/gdpr.php.

Setting Description Default
gdpr_enabled Enable or disable the cookie consent banner globally. true
gdpr_cookie_lifetime How many days the consent preference cookie is remembered (1–3650). 365
gdpr_log_consents Record each visitor’s consent decision to the database for compliance auditing. true
gdpr_banner_position Where the banner appears on the page (7 positions available). bottom-center
gdpr_privacy_policy_url URL to your privacy policy page, displayed as a link in the banner. (empty)
gdpr_category_analytics Show the Analytics cookie category toggle in the preferences panel. false
gdpr_category_marketing Show the Marketing cookie category toggle in the preferences panel. false
gdpr_category_preferences Show the Preferences cookie category toggle in the preferences panel. false

Config File Defaults

The config/gdpr.php file provides default values that are used when no database setting exists:

Admin: Settings

The settings page (GDPR → Settings) is organized into three main areas:

General Settings

The left column (7/12 width) contains the core configuration options:

  • Enable Cookie Banner: toggle switch to enable/disable the banner globally. When disabled, no banner is shown and no middleware injection occurs.
  • Cookie Lifetime (Days): numeric input (1–3650 days) controlling how long the consent cookie persists in the visitor’s browser.
  • Banner Position: visual position picker with 7 positions displayed on a browser mockup. Click a position to select it; the label updates in real time.
  • Log Consent Actions: toggle to enable/disable consent logging to the database. When enabled, every Accept/Reject/Customize/Withdraw action is recorded.
  • Privacy Policy URL: text input for the URL to your privacy policy page. If provided, a “Privacy Policy” link appears in the banner.

Banner Position Picker

The position picker is an interactive visual element showing a miniature browser window. Seven clickable dots represent the available positions:

Position Layout Description
top-left Compact card Floating card in the top-left corner (max 420px wide)
top-center Full-width bar Horizontal bar across the top of the page
top-right Compact card Floating card in the top-right corner (max 420px wide)
middle-center Modal dialog Centered modal with a semi-transparent backdrop overlay
bottom-left Compact card Floating card in the bottom-left corner (max 420px wide)
bottom-center Full-width bar Horizontal bar across the bottom of the page (default)
bottom-right Compact card Floating card in the bottom-right corner (max 420px wide)

Cookie Categories

The right column (5/12 width) displays the four cookie categories with toggle switches:

  • Essential: always on, cannot be disabled (displayed with an “Always On” badge). These are cookies required for the website to function (sessions, CSRF tokens, etc.).
  • Analytics: toggleable. When enabled, visitors can opt in/out of analytics cookies (Google Analytics, Hotjar, etc.).
  • Marketing: toggleable. When enabled, visitors can opt in/out of marketing cookies (Facebook Pixel, ad retargeting, etc.).
  • Preferences: toggleable. When enabled, visitors can opt in/out of preference cookies (language, theme, personalization, etc.).
Note: Disabling a category in admin removes it from the banner entirely: visitors won’t see the toggle for that category in the preferences panel.

Consent Overview Stats

At the top of the settings page, five stat cards show consent metrics for the last 30 days:

  • Total Consents: total number of consent actions recorded
  • Accepted All: visitors who clicked “Accept All”
  • Rejected All: visitors who clicked “Reject All”
  • Customized: visitors who saved custom preferences
  • Acceptance Rate: percentage of “Accepted All” vs total consents

The Consent Logs page (GDPR → Consent Logs) provides a full audit trail of every visitor consent decision, as required by GDPR Article 7.

Four mini stat cards at the top of the page:

  • Total Consents (neutral color)
  • Accepted All (success/green color)
  • Rejected All (danger/red color)
  • Acceptance Rate (accent color, shown as percentage)

A paginated table (25 records per page) with the following columns:

Column Description
Visitor User avatar + name/email (if authenticated) or “Guest” label (if anonymous)
IP Address Visitor’s IP address, displayed in monospace/code formatting
Action Color-coded badge: Accepted All (green), Rejected All (red), Customized (yellow), Withdrew (gray)
Categories Inline badges for each category showing enabled (filled) or disabled (outline) state
Date Human-readable relative time (e.g. “2 hours ago”) with full date in a tooltip

Filters

  • Search: filter by IP address, user name, or user email
  • Action: dropdown to filter by consent action type (accepted_all, rejected_all, customized, withdrew)

Empty State

When no logs exist, a friendly empty state is displayed with a large icon and a message explaining that records will appear once visitors interact with the banner.

The cookie consent banner is a self-contained HTML/CSS/JS component that is automatically injected into every front-end page via the InjectCookieBanner middleware.

Main Banner View

The main view displays:

  • Cookie icon: an inline SVG cookie illustration
  • Title: “Cookie Preferences”
  • Message: a brief explanation of cookie usage
  • Privacy Policy link: shown only if a URL is configured in settings
  • Three action buttons:
    • Accept All (primary dark button): accepts all cookie categories
    • Reject All (secondary gray button): rejects all non-essential cookies
    • Customize (ghost/text button): opens the preferences panel

The banner adapts its layout based on the configured position:

  • Full-width positions (top-center, bottom-center) use a horizontal flex layout with content and buttons side by side. Max width 860px, centered.
  • Corner positions (top-left, top-right, bottom-left, bottom-right) use a compact card layout (max 420px) with stacked content.
  • Modal position (middle-center) centers the banner with a semi-transparent backdrop overlay, functioning as a modal dialog (max 520px).

Preferences Panel

Clicking “Customize” transitions from the main view to the preferences panel (no page reload). The panel shows:

  • Back button with chevron icon: returns to the main banner view
  • Category list (scrollable, max height 280px) with one row per enabled category:
    • Category name and description
    • Required categories: display an “Always On” badge (no toggle)
    • Optional categories: display a custom CSS toggle switch
  • Save Preferences button: saves the selected categories and dismisses the banner

Animations

The banner uses smooth CSS animations:

  • Slide in from bottom for bottom positions
  • Slide in from top for top positions
  • Fade + scale for the center modal position
  • Reverse animations when the banner is dismissed

Responsive Design

On screens below 768px:

  • Full-width bars switch to vertical (stacked) layout
  • Corner cards expand to full width
  • Action buttons stack vertically and fill the width

Translations

Translation files are provided for English (en) and French (fr). All strings use the gdpr:: namespace prefix.

Translation Keys

Section Key Pattern Description
Categories gdpr::gdpr.categories.* Category names and descriptions (necessary, analytics, marketing, preferences)
Actions gdpr::gdpr.actions.* Consent action labels (accepted_all, rejected_all, customized, withdrew)
Positions gdpr::gdpr.positions.* Banner position labels for the admin picker
Front-end gdpr::gdpr.front.* Banner UI strings (title, message, button labels, privacy link)
Admin Settings gdpr::gdpr.admin.settings.* Settings page labels, hints, and section headings
Admin Consent Logs gdpr::gdpr.admin.consent_logs.* Consent logs page labels, column headers, empty state
Admin Stats gdpr::gdpr.admin.stats.* Stat card labels (total_consents, accepted_all, etc.)
Messages gdpr::gdpr.admin.messages.* Flash messages (settings_updated)

Updating

Step 1: Replace Files

Replace the add-on directory with the new version.

Step 2: Run Migrations

Step 3: Clear Caches

Step 4: Verify

Visit GDPR → Settings in the admin panel to confirm all settings are intact and the banner is working correctly on the front-end.

Backup first: Always back up your database before running migrations on a production system.

Troubleshooting

Banner not appearing on the front-end

Check the following:

  • The add-on is activated in Admin → Add-ons.
  • The gdpr_enabled setting is set to 1 (enabled) in settings.
  • You don’t already have a gdpr_consent cookie set. Clear cookies or use an incognito window to test.
  • The page is a standard HTML response with a </body> tag (the middleware looks for this tag to inject the banner).
  • You are viewing a front-end page, not an admin page (admin routes are excluded).

Banner appears on admin pages

This should not happen: the middleware explicitly skips admin routes via is_admin_panel_route(). If it does occur, check that:

  • The is_admin_panel_route() helper function is defined and working in your Larapen core installation.
  • Your admin route prefix matches the configured value in config('larapen.core.admin_prefix').

Consent cookie is encrypted / unreadable by JavaScript

The add-on explicitly excludes the gdpr_consent cookie from Laravel’s EncryptCookies middleware. If the cookie is still encrypted:

  • Ensure the GdprServiceProvider is properly registered (add-on is active).
  • Check that no custom middleware or package is re-encrypting the cookie.
  • Clear your browser cookies and consent again: old encrypted cookies from a previous configuration may persist.

Consent logs not being recorded

  • Check that gdpr_log_consents is set to 1 (enabled) in the admin settings.
  • Verify the gdpr_consent_logs table exists (run php artisan migrate).
  • Check your server logs for database errors (e.g. permission issues).

Banner position not changing

  • After changing the position in admin settings, make sure to clear the gdpr_consent cookie (or use an incognito window): the banner only appears for visitors without a consent cookie.
  • Clear the view cache: php artisan view:clear.

Categories not showing in preferences panel

Only categories that are enabled in the admin settings appear in the preferences panel. The “Essential” category always appears. For optional categories:

  • Check that gdpr_category_analytics, gdpr_category_marketing, and/or gdpr_category_preferences are set to 1 in settings.
  • Disabled categories are filtered out by the InjectCookieBanner middleware before rendering the view.

Log retention / purging

The GdprService::purgeOldLogs() method deletes logs older than the configured retention period (log_retention_days, default: 730 days / 2 years). This method is not currently hooked to a scheduled task. To purge old logs:

  • Call it manually from an Artisan command or tinker: app(GdprService::class)->purgeOldLogs()
  • Or add it to your application’s scheduler in routes/console.php:

CSRF token mismatch on consent endpoints

All consent endpoints (/gdpr/accept-all, etc.) require a valid CSRF token. The banner JavaScript includes the token from the rendered Blade view. If you see 419 errors:

  • Ensure your Laravel session is configured correctly.
  • Check that the web middleware group (which includes VerifyCsrfToken) is applied to the consent routes.
  • If using a custom banner view, make sure it passes the CSRF token in the X-CSRF-TOKEN header of fetch requests.

Was this article helpful?

Thank you for your feedback!

Still need help? Create a support ticket

Create a Ticket