Generator für selbstgehostetes Cookie-Consent-Banner
Beantworten Sie ein paar Fragen und erhalten Sie ein Cookie-Consent-Banner, das Ihnen gehört: reines HTML, CSS und Vanilla-JavaScript zum direkten Einfügen in Ihre eigene Website. Es blockiert nicht-essenzielle Skripte wirklich, bis jemand zustimmt, speichert die Wahl in localStorage und platziert ein ebenso auffälliges „Alle ablehnen“ neben „Alle akzeptieren“ - genau das Detail, das Regulierungsbehörden wie die französische CNIL ausdrücklich durchsetzen. Keine Drittanbieter-Consent-Plattform, kein Konto, kein Pageview-Limit.
Alles wird in deinem Browser generiert. Der Code, den du erhältst, stellt selbst keine Netzwerkanfragen - kein CDN, kein Consent-Management-Server, kein Seitenaufruf-Zähler, der ausgehen könnte.
Wird im Bannertext angezeigt - Besucher müssen wissen, wer fragt.
Erforderlich. Ohne sie ist die Einwilligung nicht informiert.
Cookie-Kategorien, die du festlegst
Strictly necessary
Von der Einwilligung ausgenommen, daher immer vorhanden und kann nicht deaktiviert werden.
Die CNIL betrachtet etwa 6 Monate als gute Praxis, sowohl für eine Ablehnung als auch für eine Einwilligung.
Verfügbar als window.cookieConsent und gespeichert unter "cookieConsent.v1".
Zwei Dinge, die du hier nicht abschalten kannst
„Alle ablehnen“ wird auf der ersten Ebene des Banners immer neben „Alle akzeptieren“ angezeigt, in gleicher Größe und Gewichtung - und optionale Kategorien werden immer unangekreuzt erzeugt. Das sind die zwei Details, für die Aufsichtsbehörden Websites tatsächlich mit Bußgeldern belegen, daher gibt es bewusst keine Option, sie zu umgehen.
- BehebenAdd your site or company name - the banner has to say who is asking.
Vorschau
We use cookies
This site uses cookies that are strictly necessary to run the site. With your permission we would also like to set optional cookies. You can accept them all, refuse them all, or choose category by category.
<style>
/* Cookie consent banner - generated by usezivo.com.
Dependency-free: no fonts, no CDN, no network requests of its own. */
.cc-cookieconsent-root { position: fixed; z-index: 2147483000; }
.cc-cookieconsent-backdrop {
position: fixed; inset: 0; z-index: 2147482999;
background: rgba(15, 23, 42, 0.45);
}
.cc-cookieconsent-banner {
position: fixed;
left: 0; right: 0; bottom: 0; max-width: none; border-radius: 0; border-left: 0; border-right: 0; border-bottom: 0;
box-sizing: border-box;
padding: 18px 20px;
background: #ffffff;
color: #0f172a;
border: 1px solid #e2e8f0;
border-radius: 14px;
box-shadow: 0 10px 40px rgba(15, 23, 42, 0.18);
font: 400 14px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.cc-cookieconsent-title { margin: 0 0 6px; font-size: 15px; font-weight: 700; }
.cc-cookieconsent-text { margin: 0 0 14px; color: #475569; }
.cc-cookieconsent-text a { color: #2563eb; text-decoration: underline; }
/* Accept and Reject are deliberately the SAME component: same size, same
weight, same visual prominence, side by side at the first level. */
.cc-cookieconsent-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.cc-cookieconsent-btn {
appearance: none;
flex: 0 0 auto;
min-width: 148px;
padding: 11px 18px;
font: inherit;
font-weight: 600;
line-height: 1.2;
border-radius: 10px;
border: 1px solid #2563eb;
background: #2563eb;
color: #ffffff;
cursor: pointer;
}
.cc-cookieconsent-btn:hover { filter: brightness(0.94); }
.cc-cookieconsent-btn:focus-visible { outline: 3px solid #2563eb; outline-offset: 2px; }
.cc-cookieconsent-btn--secondary { background: transparent; color: #2563eb; }
.cc-cookieconsent-link {
appearance: none; background: none; border: 0; padding: 8px 4px;
font: inherit; color: #475569; text-decoration: underline; cursor: pointer;
}
.cc-cookieconsent-link:hover { color: #0f172a; }
.cc-cookieconsent-panel {
position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
box-sizing: border-box;
width: min(620px, calc(100vw - 32px));
max-height: min(760px, calc(100vh - 32px));
overflow-y: auto;
padding: 22px;
background: #ffffff; color: #0f172a;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(15, 23, 42, 0.3);
font: 400 14px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.cc-cookieconsent-panel h2 { margin: 0 0 6px; font-size: 17px; }
.cc-cookieconsent-panel-intro { margin: 0 0 16px; color: #475569; }
.cc-cookieconsent-cat {
padding: 14px 0;
border-top: 1px solid #e2e8f0;
}
.cc-cookieconsent-cat-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.cc-cookieconsent-cat-name { font-weight: 600; }
.cc-cookieconsent-cat-desc { margin: 6px 0 0; color: #64748b; font-size: 13px; }
.cc-cookieconsent-always { font-size: 12px; font-weight: 600; color: #16a34a; }
/* Switch */
.cc-cookieconsent-switch { position: relative; display: inline-block; width: 44px; height: 24px; flex: 0 0 auto; }
.cc-cookieconsent-switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.cc-cookieconsent-slider {
position: absolute; inset: 0; border-radius: 999px;
background: #cbd5e1; transition: background 0.15s ease; pointer-events: none;
}
.cc-cookieconsent-slider::after {
content: ""; position: absolute; top: 3px; left: 3px;
width: 18px; height: 18px; border-radius: 50%; background: #fff;
transition: transform 0.15s ease;
}
.cc-cookieconsent-switch input:checked + .cc-cookieconsent-slider { background: #2563eb; }
.cc-cookieconsent-switch input:checked + .cc-cookieconsent-slider::after { transform: translateX(20px); }
.cc-cookieconsent-switch input:focus-visible + .cc-cookieconsent-slider { outline: 3px solid #2563eb; outline-offset: 2px; }
.cc-cookieconsent-panel-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; padding-top: 16px; border-top: 1px solid #e2e8f0; }
@media (max-width: 520px) {
.cc-cookieconsent-banner { left: 8px; right: 8px; bottom: 8px; max-width: none; border-radius: 14px; border: 1px solid #e2e8f0; transform: none; top: auto; }
.cc-cookieconsent-btn { flex: 1 1 100%; min-width: 0; }
}
@media (prefers-color-scheme: dark) {
.cc-cookieconsent-banner, .cc-cookieconsent-panel { background: #0f172a; color: #e2e8f0; border-color: #334155; }
.cc-cookieconsent-text, .cc-cookieconsent-panel-intro, .cc-cookieconsent-cat-desc { color: #94a3b8; }
.cc-cookieconsent-cat, .cc-cookieconsent-panel-actions { border-color: #334155; }
.cc-cookieconsent-link { color: #94a3b8; }
.cc-cookieconsent-link:hover { color: #e2e8f0; }
.cc-cookieconsent-slider { background: #475569; }
@media (max-width: 520px) { .cc-cookieconsent-banner { border-color: #334155; } }
}
</style>
<!-- Cookie consent banner - generated by usezivo.com. Paste before </body>. -->
<div id="cc-cookieconsent-banner" class="cc-cookieconsent-banner" role="dialog" aria-modal="false" aria-labelledby="cc-cookieconsent-title" hidden>
<p class="cc-cookieconsent-title" id="cc-cookieconsent-title">We use cookies</p>
<p class="cc-cookieconsent-text">This site uses cookies that are strictly necessary to run the site. With your permission we would also like to set optional cookies. You can accept them all, refuse them all, or choose category by category. <a href="/privacy">Privacy policy</a></p>
<div class="cc-cookieconsent-actions">
<button type="button" class="cc-cookieconsent-btn" data-cc-cookieconsent-action="accept">Accept all</button>
<button type="button" class="cc-cookieconsent-btn" data-cc-cookieconsent-action="reject">Reject all</button>
<button type="button" class="cc-cookieconsent-link" data-cc-cookieconsent-action="open">Customise</button>
</div>
</div>
<div id="cc-cookieconsent-panel" class="cc-cookieconsent-panel" role="dialog" aria-modal="true" aria-labelledby="cc-cookieconsent-panel-title" hidden>
<h2 id="cc-cookieconsent-panel-title">Cookie preferences</h2>
<p class="cc-cookieconsent-panel-intro">Strictly necessary cookies cannot be switched off. Every other category is off until you turn it on.</p>
<div class="cc-cookieconsent-cat">
<div class="cc-cookieconsent-cat-head">
<span class="cc-cookieconsent-cat-name">Strictly necessary</span>
<span class="cc-cookieconsent-always">Always active</span>
</div>
<p class="cc-cookieconsent-cat-desc">Needed for the site to work: security, load balancing, remembering what is in your basket and storing this cookie choice. These are exempt from consent.</p>
</div>
<div class="cc-cookieconsent-cat">
<div class="cc-cookieconsent-cat-head">
<span class="cc-cookieconsent-cat-name">Analytics</span>
<label class="cc-cookieconsent-switch">
<input type="checkbox" data-cc-cookieconsent-cat="analytics" aria-label="Analytics">
<span class="cc-cookieconsent-slider"></span>
</label>
</div>
<p class="cc-cookieconsent-cat-desc">Help us understand which pages are visited and where people get stuck, so we can improve the site. Measurement only, never used to target advertising.</p>
</div>
<div class="cc-cookieconsent-panel-actions">
<button type="button" class="cc-cookieconsent-btn" data-cc-cookieconsent-action="save">Save my choices</button>
<button type="button" class="cc-cookieconsent-btn cc-cookieconsent-btn--secondary" data-cc-cookieconsent-action="accept">Accept all</button>
<button type="button" class="cc-cookieconsent-btn cc-cookieconsent-btn--secondary" data-cc-cookieconsent-action="reject">Reject all</button>
</div>
</div>
<script>
/* Cookie consent - generated by usezivo.com. Vanilla JS, no dependencies,
no network requests. Paste after the markup, or load as an external file. */
(function () {
"use strict";
var KEY = "cookieConsent.v1";
var MAX_AGE_MS = 183 * 24 * 60 * 60 * 1000;
var OPTIONAL = ["analytics"];
var PREFIX = "cc-cookieconsent";
var banner = document.getElementById(PREFIX + "-banner");
var panel = document.getElementById(PREFIX + "-panel");
var backdrop = null;
var lastFocus = null;
// --- storage ------------------------------------------------------------
// localStorage can throw outright in some privacy modes, so every access is
// guarded. If it is unavailable the banner simply reappears next visit,
// which is the safe direction to fail in: nothing gets unblocked.
function read() {
try {
var raw = localStorage.getItem(KEY);
if (!raw) return null;
var saved = JSON.parse(raw);
if (!saved || typeof saved !== "object") return null;
if (typeof saved.at !== "number" || Date.now() - saved.at > MAX_AGE_MS) return null;
var out = { necessary: true };
for (var i = 0; i < OPTIONAL.length; i++) {
out[OPTIONAL[i]] = saved.categories && saved.categories[OPTIONAL[i]] === true;
}
return out;
} catch (e) {
return null;
}
}
function write(categories) {
try {
localStorage.setItem(KEY, JSON.stringify({ v: 1, at: Date.now(), categories: categories }));
} catch (e) {
/* storage blocked - the choice applies to this page load only */
}
}
var state = read();
// --- script gating ------------------------------------------------------
// Third-party tags are written as:
// <script type="text/plain" data-cookie-category="analytics" src="..."></script>
// The browser will not execute type="text/plain", so nothing runs until the
// matching category is granted, at which point the tag is swapped for a real
// one. Inline tags work the same way.
function unblock(granted) {
var pending = document.querySelectorAll('script[type="text/plain"][data-cookie-category]');
for (var i = 0; i < pending.length; i++) {
var node = pending[i];
var category = node.getAttribute("data-cookie-category");
if (!granted[category]) continue;
var real = document.createElement("script");
for (var a = 0; a < node.attributes.length; a++) {
var attr = node.attributes[a];
if (attr.name === "type" || attr.name === "data-cookie-category") continue;
real.setAttribute(attr.name, attr.value);
}
real.type = "text/javascript";
if (!node.src) real.text = node.text;
node.parentNode.replaceChild(real, node);
}
}
var listeners = [];
function emit(granted) {
unblock(granted);
for (var i = 0; i < listeners.length; i++) {
try {
listeners[i](granted);
} catch (e) {
/* one bad listener must not break the rest */
}
}
try {
document.dispatchEvent(new CustomEvent("cookieConsent:change", { detail: granted }));
} catch (e) {
/* CustomEvent unsupported - the callback API still works */
}
}
// --- UI -----------------------------------------------------------------
function showBanner() {
if (!banner) return;
banner.hidden = false;
/* no backdrop configured */
}
function hideBanner() {
if (banner) banner.hidden = true;
if (backdrop && backdrop.parentNode) {
backdrop.parentNode.removeChild(backdrop);
backdrop = null;
}
}
function boxes() {
return panel ? panel.querySelectorAll("[data-" + PREFIX + "-cat]") : [];
}
function openPanel() {
if (!panel) return;
lastFocus = document.activeElement;
// Reflect the stored choice, or leave every optional box OFF when there is
// no stored choice yet. Nothing is ever pre-ticked.
var current = read();
var list = boxes();
for (var i = 0; i < list.length; i++) {
var id = list[i].getAttribute("data-" + PREFIX + "-cat");
list[i].checked = !!(current && current[id]);
}
panel.hidden = false;
var first = panel.querySelector("button, input");
if (first) first.focus();
document.addEventListener("keydown", onKeydown);
}
function closePanel() {
if (!panel) return;
panel.hidden = true;
document.removeEventListener("keydown", onKeydown);
if (lastFocus && lastFocus.focus) lastFocus.focus();
}
function onKeydown(e) {
// Escape closes the PREFERENCES panel only. It never dismisses the banner:
// dismissing without choosing must not be treated as consent.
if (e.key === "Escape") closePanel();
}
function decide(categories) {
var granted = { necessary: true };
for (var i = 0; i < OPTIONAL.length; i++) {
granted[OPTIONAL[i]] = categories[OPTIONAL[i]] === true;
}
state = granted;
write(granted);
closePanel();
hideBanner();
emit(granted);
}
function all(value) {
var out = {};
for (var i = 0; i < OPTIONAL.length; i++) out[OPTIONAL[i]] = value;
return out;
}
function fromPanel() {
var out = {};
var list = boxes();
for (var i = 0; i < list.length; i++) {
out[list[i].getAttribute("data-" + PREFIX + "-cat")] = list[i].checked === true;
}
return out;
}
document.addEventListener("click", function (e) {
var target = e.target.closest ? e.target.closest("[data-" + PREFIX + "-action]") : null;
if (!target) return;
var action = target.getAttribute("data-" + PREFIX + "-action");
if (action === "accept") decide(all(true));
else if (action === "reject") decide(all(false));
else if (action === "save") decide(fromPanel());
else if (action === "open") openPanel();
else if (action === "close") closePanel();
});
// --- public API ---------------------------------------------------------
window["cookieConsent"] = {
/** true if that category has been granted. Always true for "necessary". */
has: function (category) {
if (category === "necessary") return true;
return !!(state && state[category]);
},
/** The full granted map, or null if no choice has been made yet. */
get: function () {
return state ? JSON.parse(JSON.stringify(state)) : null;
},
/** Run cb now (if a choice exists) and on every later change. Returns an unsubscribe fn. */
onChange: function (cb) {
listeners.push(cb);
if (state) {
try {
cb(state);
} catch (e) {
/* ignore */
}
}
return function () {
var i = listeners.indexOf(cb);
if (i > -1) listeners.splice(i, 1);
};
},
/** Wire this to a permanent "Cookie preferences" link in your footer. */
openPreferences: openPanel,
/** Forget the stored choice and ask again. Withdrawal must be as easy as consent. */
reset: function () {
try {
localStorage.removeItem(KEY);
} catch (e) {
/* ignore */
}
state = null;
showBanner();
},
};
// --- boot ---------------------------------------------------------------
if (state) {
// A stored choice: apply it silently, do not nag.
emit(state);
} else {
// No choice yet -> nothing is unblocked, and the banner asks.
showBanner();
}
})();
</script>Der Code aktualisiert sich live, aber behebe die obigen Punkte, bevor du ihn live schaltest.
Woher die obigen Regeln stammen
- CNIL - Questions-réponses sur les lignes directrices et la recommandation « cookies et autres traceurs »« Il doit toutefois être aussi facile d'accepter que de refuser les traceurs. » Continuing to browse is no longer valid consent and must be read as refusal; withdrawing consent must be as simple as giving it; ~6 months is good practice for storing the choice.
- CNIL - Recommandation « cookies et autres traceurs » (consolidated PDF)The recommendation itself: a « Tout refuser » button at the same level and with the same appearance as « Tout accepter » is a clear and simple way of letting users express their choice.
- CNIL - Cookies: la CNIL incite les organismes à auditer leurs sites webCNIL's own audit checklist, including the refusal-as-easy-as-acceptance point and the exemption for strictly necessary cookies.
Dieser Generator erzeugt die Banner-Oberfläche und den Mechanismus zur Speicherung der Einwilligung. Das ist keine Rechtsberatung und kann allein keine Website rechtskonform machen - entscheidend ist, welche Tracker du einsetzt, ob du sie tatsächlich über das Gate geschaltet hast und was deine Datenschutzerklärung aussagt. Die Regeln unterscheiden sich außerdem von Land zu Land und ändern sich mit der Zeit. Wenn viel auf dem Spiel steht, lass es von einem Anwalt prüfen.
Warum selbst gehostet statt Axeptio oder Cookiebot
Gehostete Consent-Plattformen sind ausgezeichnete Produkte, aber ihre kostenlosen Stufen sind limitiert: Überschreitet man eine monatliche Seitenaufruf-Schwelle, funktioniert das Banner entweder nicht mehr, oder die Abrechnung beginnt. Außerdem laden sie bei jedem einzelnen Seitenaufruf ihr eigenes JavaScript von ihrer eigenen Domain, was im Namen des Datenschutzes eine Drittanbieter-Anfrage zur eigenen Seite hinzufügt - und eine Abhängigkeit, die sich ohne eigene Kontrolle ändern kann.
Das Snippet, das dieses Tool erzeugt, ist etwa 6 KB groß, hat keine Abhängigkeiten, stellt selbst keine Netzwerkanfragen und liegt im eigenen Repository, wo man jede einzelne Zeile lesen kann. Es gibt keinen Anbieter, dessen Kontingent ausgehen könnte, und nichts sendet heimlich Daten nach Hause.
| Was das Banner tut | Wie |
|---|---|
| Blockiert Tags vor der Einwilligung | Drittanbieter-Tags werden als type="text/plain" mit einem data-cookie-category-Attribut geschrieben, was Browser auszuführen verweigern. Nach der Einwilligung wird das Tag durch ein echtes Script-Element ersetzt, und erst dann läuft es. |
| Ablehnen ist genauso einfach wie Zustimmen | „Alle akzeptieren“ und „Alle ablehnen“ sind dieselbe Button-Komponente - gleiche Breite, gleicher Innenabstand, gleiche Schriftstärke und Farbe - nebeneinander auf der ersten Ebene. |
| Nichts ist vorab angekreuzt | Die erzeugten Präferenzschalter tragen nie ein checked-Attribut. Niemals. |
| Schweigen ist keine Einwilligung | Das Wegklicken oder Ignorieren des Banners speichert nichts und schaltet nichts frei. Nur ein ausdrücklicher Klick schreibt eine Entscheidung. |
| Widerruf jederzeit möglich | <ns/>.openPreferences() öffnet das Panel erneut, und .reset() vergisst die Entscheidung - verknüpfe eines der beiden mit einem Link im Footer. |
| Merkt sich die Antwort | localStorage, mit einem Zeitstempel und einer selbst gewählten Ablaufzeit (standardmäßig 6 Monate), damit Besucher nicht auf jeder Seite erneut gefragt werden. |
Häufig gestellte Fragen
Wie erstelle ich ein kostenloses, konformes Cookie-Banner ohne eine kostenpflichtige Plattform?
Geben Sie den Namen Ihrer Website, den Link zu Ihrer Datenschutzerklärung und die Cookie-Kategorien ein, die Sie tatsächlich verwenden, und kopieren Sie dann den generierten Block vor das schließende body-Tag Ihrer Seite. Sie erhalten reines HTML, CSS und JavaScript ohne Abhängigkeit von Axeptio, Cookiebot, Tarteaucitron oder einer anderen Consent-Plattform - also kein Pageview-Limit der kostenlosen Stufe und kein Drittanbieter-Skript, das auf jeder Seite lädt.
Blockiert es Google Analytics wirklich vor der Zustimmung, oder versteckt es nur das Banner?
Es blockiert wirklich, aber Sie müssen eine Sache tun: Ändern Sie Ihr Tag von type="text/javascript" zu type="text/plain" und fügen Sie data-cookie-category="analytics" hinzu. Ein Browser führt kein text/plain-Skript aus, sodass das Tag inert auf der Seite liegt. Sobald der Besucher diese Kategorie gewährt, wird das Skript in ein echtes Script-Element geklont und ausgeführt.
Warum gibt es keine Option, den Button „Alle ablehnen“ zu verstecken?
Weil das das meistgenannte Problem bei nicht konformen Bannern ist. Die CNIL vertritt die Position, dass es ebenso einfach sein muss, Tracker abzulehnen wie sie zu akzeptieren, und betrachtet einen Button „Alle ablehnen“ auf der ersten Ebene, mit dem gleichen Erscheinungsbild wie „Alle akzeptieren“, als den klaren Weg dorthin. Dieser Generator erzeugt beide Buttons immer mit derselben CSS-Klasse, sodass sie identisch in Größe und Gewicht sind.
Macht das meine Website rechtlich DSGVO-konform?
Nein, und seien Sie vorsichtig bei jedem Tool, das etwas anderes behauptet. Dies erzeugt die Banner-Oberfläche und den Mechanismus zur Speicherung der Zustimmung. Die Konformität hängt auch davon ab, welche Tracker Sie tatsächlich einsetzen, ob jeder davon wirklich durch das Gate läuft, was Ihre Datenschutz- und Cookie-Richtlinien besagen, und von Regeln, die je nach Land variieren und sich im Laufe der Zeit ändern.