stevecreekmore / cookies
A Laravel package for managing cookie consent and GDPR compliance
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Language:Blade
pkg:composer/stevecreekmore/cookies
Requires
- php: ^8.4
- illuminate/contracts: ^12.0
- illuminate/support: ^12.0
Requires (Dev)
- laravel/pint: ^1.0
- pestphp/pest: ^4.0
- spatie/ray: ^1.28
This package is auto-updated.
Last update: 2025-12-31 02:54:50 UTC
README
A GDPR-compliant Laravel package for managing cookie consent with category-based consent management, script blocking, consent logging, and easy withdrawal.
Features
- ✅ GDPR Compliant - Meets all major GDPR requirements
- 🚫 Script Blocking - Prevents non-consented scripts from loading
- 📝 Consent Logging - Database audit trail of all consent actions
- 🔄 Easy Withdrawal - Users can change their mind anytime
- 📊 Detailed Cookie Information - Show specific cookies, purposes, and durations
- 🎨 Fully Customizable - Themes, positioning, and text
- 🔌 Blade Directives - Easy integration with existing code
- 🌐 Event System - React to consent changes in JavaScript
- 📱 Responsive Design - Works on all devices
GDPR Compliance
This package helps you comply with GDPR by:
- Blocking Non-Essential Cookies - Scripts are blocked until consent is given
- Granular Consent - Users can choose specific cookie categories
- Detailed Information - Shows cookie names, purposes, durations, and providers
- Easy Withdrawal - Floating button to change preferences anytime
- Consent Logging - Audit trail stored in database (proof of consent)
- Privacy Policy Links - Direct links to your policies
- Opt-in by Default - No pre-checked boxes (except necessary cookies)
Installation
Install the package via composer:
composer require stevecreekmore/cookies
The package will automatically register its service provider.
Publish Configuration
Publish the configuration file:
php artisan vendor:publish --tag=cookies-config
Publish Migrations (for consent logging)
php artisan vendor:publish --tag=cookies-migrations php artisan migrate
Publish Views (Optional)
If you want to customize the banner appearance:
php artisan vendor:publish --tag=cookies-views
Add Middleware
Add the middleware to your bootstrap/app.php (Laravel 11+):
->withMiddleware(function (Middleware $middleware) { $middleware->web(append: [ \Stevecreekmore\Cookies\Middleware\AppendCookieConsentToResponse::class, ]); })
Or for Laravel 10 and below, add to app/Http/Kernel.php:
protected $middlewareGroups = [ 'web' => [ // ... \Stevecreekmore\Cookies\Middleware\AppendCookieConsentToResponse::class, ], ];
Configuration
Define Your Cookies
Edit config/cookies.php to define the specific cookies your site uses:
'categories' => [ 'necessary' => [ 'enabled' => true, 'required' => true, 'label' => 'Necessary', 'description' => 'These cookies are essential for the website to function properly.', 'cookies' => [ 'session' => [ 'name' => 'laravel_session', 'purpose' => 'Maintains user session state', 'duration' => '2 hours', 'provider' => 'This website', ], 'csrf' => [ 'name' => 'XSRF-TOKEN', 'purpose' => 'Security token to prevent cross-site request forgery', 'duration' => '2 hours', 'provider' => 'This website', ], ], ], 'analytics' => [ 'enabled' => true, 'required' => false, 'label' => 'Analytics', 'description' => 'These cookies help us understand how visitors interact with our website.', 'cookies' => [ 'google_analytics' => [ 'name' => '_ga, _gid, _gat', 'purpose' => 'Used to distinguish users and sessions for analytics', 'duration' => '2 years (_ga), 24 hours (_gid)', 'provider' => 'Google LLC', ], ], ], // Add more categories... ],
Set Policy URLs
'policy_url' => env('COOKIE_POLICY_URL', '/privacy-policy'), 'cookie_policy_url' => env('COOKIE_POLICY_URL', '/cookie-policy'),
Or in your .env:
COOKIE_CONSENT_ENABLED=true COOKIE_CONSENT_LOG=true COOKIE_POLICY_URL=/privacy-policy COOKIE_POLICY_URL=/cookie-policy
Usage
Blocking Scripts Until Consent
The most important GDPR feature - scripts won't load until the user consents:
Method 1: Blade Directive (Recommended)
@cookieConsentScript('analytics') // Google Analytics (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-XXXXX-Y', 'auto'); ga('send', 'pageview'); @endCookieConsentScript
Method 2: Manual Script Blocking
<script type="text/plain" data-cookie-consent="marketing"> // Facebook Pixel code !function(f,b,e,v,n,t,s){/* ... */} </script>
The script will automatically be activated once the user consents to that category.
Checking Consent in PHP
use Stevecreekmore\Cookies\Facades\Cookies; // Check specific category if (Cookies::hasConsent('analytics')) { // Load analytics } // Get all consented categories $categories = Cookies::getConsent(); // Returns: ['necessary', 'analytics'] // Check if any consent given if (Cookies::hasGivenConsent()) { // User has interacted with banner } // Check if all categories accepted if (Cookies::hasAcceptedAll()) { // User clicked "Accept All" }
Blade Conditional Directive
@cookieConsent('marketing') <script src="https://connect.facebook.net/en_US/fbevents.js"></script> @endcookieConsent @cookieConsent('analytics') <!-- Google Analytics tracking --> @endcookieConsent
JavaScript Event Listener
React to consent changes in your JavaScript:
window.addEventListener('cookieConsentChanged', function(event) { const { categories, action, id } = event.detail; console.log('Consent action:', action); // 'accept_all', 'reject_all', or 'custom' console.log('Consented categories:', categories); console.log('Consent ID:', id); // Load scripts dynamically if (categories.includes('analytics')) { initializeAnalytics(); } if (categories.includes('marketing')) { initializeMarketing(); } });
GDPR Compliance Features
1. Consent Logging (Audit Trail)
All consent actions are logged to the database:
use Stevecreekmore\Cookies\Models\CookieConsentLog; // Get consent history for a user $history = CookieConsentLog::where('cookie_id', $consentId) ->latest() ->get(); // Each log contains: // - cookie_id (unique user identifier) // - consented_categories (JSON array) // - ip_address // - user_agent // - action (accept_all, reject_all, custom, withdraw) // - timestamp
2. Consent Withdrawal
A floating button appears after consent is given, allowing users to:
- Change their preferences
- Withdraw consent completely
- View what they previously accepted
This is a GDPR requirement - withdrawal must be as easy as giving consent.
3. Detailed Cookie Information
Users can click "View Cookie Details" to see a table with:
- Cookie names
- Purpose of each cookie
- Duration/expiry
- Provider (first-party or third-party)
4. Log Retention
Control how long consent logs are kept (GDPR requires keeping records):
'log_retention_days' => 1095, // 3 years (default)
Clean up old logs:
use Stevecreekmore\Cookies\Models\CookieConsentLog; // Manually clean up CookieConsentLog::cleanupOldLogs();
Or schedule it in app/Console/Kernel.php:
protected function schedule(Schedule $schedule) { $schedule->call(function () { \Stevecreekmore\Cookies\Models\CookieConsentLog::cleanupOldLogs(); })->weekly(); }
Customization
Styling
Customize appearance in config/cookies.php:
'styling' => [ 'position' => 'bottom', // bottom, top, center 'theme' => 'dark', // light, dark ],
Custom Text
Change all text and labels:
'text' => [ 'title' => 'We Value Your Privacy', 'description' => 'Your custom description...', 'accept_all' => 'Accept All', 'reject_all' => 'Only Essential', // ... more text options ],
Custom Banner View
After publishing views, edit resources/views/vendor/cookies/banner.blade.php for complete control.
Disable Consent Logging
If you don't need database logging:
COOKIE_CONSENT_LOG=false
Hide Settings Button
'show_settings_button' => false,
Testing
composer test
Best Practices for GDPR Compliance
- Document All Cookies - Fill in the
cookiesarray for each category with specific cookie details - Update Your Privacy Policy - Include information about cookie usage
- Block All Non-Essential Scripts - Use the Blade directive or manual blocking
- Keep Consent Logs - Enable database logging for proof of consent
- Regular Audits - Review what cookies your site actually sets
- Honor Withdrawals - The floating button makes this easy
- Third-Party Cookies - List all third-party providers clearly
API Reference
Facade Methods
Cookies::hasConsent(string $category): bool Cookies::getConsent(): array Cookies::hasGivenConsent(): bool Cookies::hasAcceptedAll(): bool Cookies::getEnabledCategories(): array Cookies::getRequiredCategories(): array
Blade Directives
@cookieConsentScript('category') // Your script here @endCookieConsentScript @cookieConsent('category') <!-- Your content --> @endcookieConsent
JavaScript Events
// Consent changed window.addEventListener('cookieConsentChanged', function(event) { // event.detail contains: { categories, action, id } });
Troubleshooting
Scripts Not Loading After Consent
Make sure you're using type="text/plain" and the data-cookie-consent attribute:
<script type="text/plain" data-cookie-consent="analytics"> // Your script </script>
Banner Not Showing
- Check middleware is added to web routes
- Ensure
COOKIE_CONSENT_ENABLED=truein.env - Check if consent cookie already exists (delete it to test)
Consent Not Being Logged
- Run migrations:
php artisan migrate - Check
COOKIE_CONSENT_LOG=truein config - Ensure CSRF token is present on the page:
<meta name="csrf-token" content="{{ csrf_token() }}">
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.