fendinger/kirby-consent-gate

GDPR compliant external content blocker for Kirby CMS

Maintainers

Package info

github.com/fendinger/kirby-consent-gate

Type:kirby-plugin

pkg:composer/fendinger/kirby-consent-gate

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-04-15 19:41 UTC

This package is auto-updated.

Last update: 2026-04-15 19:46:28 UTC


README

Kirby Consent Gate

Kirby Consent Gate

This plugin for Kirby CMS automatically detects and blocks external content (iframes, scripts, embeds) from loading until the user gives consent. Fully GDPR compliant.

Unlike traditional consent plugins that require manual tag overrides, this plugin parses the rendered HTML output and automatically wraps any external resource in a consent gate - no matter how the content was added (blocks, tags, templates, raw HTML).

This plugin does not use cookies. Consent is stored in the browser's local storage.

This is the successor to kirby3-consent-gate, completely rewritten for modern Kirby versions.

How It Works

  1. HTML Parser scans the rendered page for external <iframe>, <script>, <embed> and <object> elements
  2. Visual embeds (iframes, embeds, objects) are replaced with a consent gate UI showing the vendor's logo, name and privacy information
  3. Scripts (e.g. Google Analytics) are neutralized with type="text/plain" and activated on consent
  4. Compound embeds (e.g. Twitter blockquote + script) are detected as a unit and gated together
  5. A vendor registry tracks which vendors were detected across all pages and displays them in the consent banner/modal

Features

  • Automatic detection of external content - no manual tag overrides needed
  • Supports iframes, scripts, embeds and objects
  • Compound embed detection (Twitter/X, Instagram)
  • Per-vendor consent via banner and modal
  • Vendor registry across all pages
  • Cookie-free (uses localStorage)
  • Standalone CSS or Tailwind CSS support
  • Built-in vendor configs with logos for 15+ services
  • Vendor allowlist to skip gating for specific vendors
  • English and German translations
  • Custom vendor configs via site/config/config.php

Supported Vendors (built-in)

Vendor Key Name URL Patterns
bluesky Bluesky bsky.app, bsky.social
facebook Facebook facebook.com, fbcdn.net
flickr Flickr flickr.com, staticflickr.com
github GitHub github.com, githubusercontent.com
googleanalytics Google Analytics googletagmanager.com, google-analytics.com
googlemaps Google Maps google.*/maps/
instagram Instagram instagram.com, cdninstagram.com
kaltura Kaltura kaltura.com, kaltura.org
mastodon Mastodon mastodon.social, mastodon.online
matomo Matomo (self-hosted, add your domain via config)
openstreetmap OpenStreetMap openstreetmap.org, osm.org
spotify Spotify spotify.com
vimeo Vimeo vimeo.com
x X/Twitter x.com, twitter.com, twimg.com
youtube YouTube youtube.com, youtu.be, youtube-nocookie.com, ytimg.com

Unknown external sources are automatically gated as unknown:hostname (configurable via gateUnknown).

Installation

Composer

composer require fendinger/kirby-consent-gate

Manual

Download and copy this repository to /site/plugins/kirby-consent-gate.

Configuration

All options are set in site/config/config.php:

Style

Choose between standalone CSS (default) or Tailwind CSS:

// Standalone CSS (default) - no dependencies
'fendinger.consent-gate.style' => 'css',

// Tailwind CSS - assumes Tailwind is active in your project
'fendinger.consent-gate.style' => 'tailwind',

Allowlist

Vendors that should never be gated (by vendor key):

'fendinger.consent-gate.allowlist' => [
    'youtube',
    'x',
],

For unknown vendors, use the unknown:hostname format:

'fendinger.consent-gate.allowlist' => [
    'unknown:cdn.example.com',
],

Gate Unknown Sources

Whether to gate external sources that don't match any known vendor:

'fendinger.consent-gate.gateUnknown' => true, // default

Gate Elements

Which HTML element types to scan:

'fendinger.consent-gate.gateElements' => ['iframe', 'script', 'embed', 'object'], // default

Privacy Page

Slug of the page to link as privacy policy in the consent modal:

'fendinger.consent-gate.privacyPage' => 'datenschutz', // default

Custom Vendors

Add or override vendor configurations:

'fendinger.consent-gate.vendors.tiktok' => [
    'name' => 'TikTok',
    'urlPatterns' => ['tiktok\.com'],
    'compoundPattern' => '<blockquote[^>]*class="[^"]*tiktok-embed[^"]*"[^>]*>.*?</blockquote>\s*<script[^>]*src="[^"]*tiktok\.com/embed\.js[^"]*"[^>]*>\s*</script>',
    'logo' => '<svg>...</svg>',
    'description' => [
        'en' => '<p>TikTok videos are embedded on our website...</p>',
        'de' => '<p>Auf unserer Website werden TikTok-Videos eingebunden...</p>',
    ],
    'privacy' => 'https://www.tiktok.com/legal/privacy-policy',
],

Vendor Config Options

Option Description
name Display name
urlPatterns Array of regex patterns matched against element URLs
compoundPattern Optional regex for multi-element embeds (e.g. blockquote + script)
logo SVG markup (48x48)
description Array with en and de keys containing HTML descriptions
privacy URL to the vendor's privacy policy

How the Parser Works

The plugin hooks into Kirby's page.render:after event and processes the HTML in two steps:

  1. Compound embeds are detected first via regex patterns (e.g. Twitter's <blockquote> + <script> combo) and wrapped in a gate
  2. Individual elements are parsed with PHP's DOMDocument. For each <iframe>, <script>, <embed> or <object> with an external URL:
    • The URL is checked against the site's own domain
    • The matched vendor is checked against the allowlist
    • The URL is matched against vendor urlPatterns
    • Iframes/embeds/objects are replaced with the gate UI
    • Scripts are neutralized with type="text/plain" and reactivated via JS on consent

Vendor Registry Updates

The vendor registry stays in sync automatically through the following hooks:

  • page.render:after — on every frontend visit, detected vendors for that page are written to the registry
  • page.create:after — newly created pages are rendered immediately so their vendors get registered right away
  • page.update:after — updated pages are re-rendered so their registry entry reflects the latest content
  • page.changeSlug:after — the old page ID is removed from the registry and the page is re-rendered under its new ID
  • page.delete:after — the deleted page's entry is removed from the registry

Because creating, updating or renaming a page triggers a full render inside the Panel request, saving may take slightly longer on very heavy pages.

Manual crawl

If the registry is ever out of sync (e.g. after a bulk content import or after installing the plugin on an existing site), you can trigger a full crawl via:

GET /consent-gate/crawl

This iterates over all pages, renders each one and returns a JSON summary with the number of rendered pages, any failures and the aggregated list of detected vendors.

Limitations

The parser runs on Kirby's page.render:after hook, which only fires when a Kirby\Cms\Page is actually rendered. As a result, the following cases are not covered:

  • Custom routes defined in site/config/config.php that return HTML directly (without calling $page->render() or site()->visit())
  • Plugin routes that return their own Response
  • API / JSON endpoints
  • Any other output that bypasses Kirby's page rendering pipeline

External resources embedded in such responses will not be gated automatically. If you use custom routes to deliver HTML, you will need to handle consent for external content there manually.

Support

If you find this plugin useful, you can support the development:

Buy Me A Coffee

License

MIT

Author

fendinger.de