Gerador de banner de consentimento de cookies auto-hospedado
Responda a algumas perguntas e obtenha um banner de consentimento de cookies que você possui: HTML, CSS e JavaScript puro para colar diretamente no seu site. Ele realmente bloqueia scripts não essenciais até que alguém consinta, guarda a escolha no localStorage, e coloca um botão "Rejeitar tudo" tão visível quanto "Aceitar tudo" - o detalhe que reguladores como a CNIL francesa exigem explicitamente. Sem plataforma de consentimento de terceiros, sem conta, sem limite de páginas vistas.
Tudo é gerado no seu navegador. O código que você recebe não faz nenhuma solicitação de rede própria - sem CDN, sem servidor de gestão de consentimento, sem contador de visualizações para esgotar.
Exibido no texto do banner - os visitantes precisam saber quem está perguntando.
Obrigatório. Sem ela, o consentimento não é informado.
Categorias de cookies que você define
Strictly necessary
Isenta de consentimento, por isso está sempre presente e não pode ser desativada.
A CNIL considera cerca de 6 meses uma boa prática, tanto para uma recusa quanto para um consentimento.
Exposta como window.cookieConsent e armazenada sob "cookieConsent.v1".
Duas coisas que você não pode desativar aqui
"Rejeitar tudo" sempre aparece ao lado de "Aceitar tudo", com o mesmo tamanho e peso, no primeiro nível do banner - e as categorias opcionais são sempre geradas desmarcadas. Esses são os dois detalhes pelos quais os reguladores realmente multam sites, então deliberadamente não há opção para violá-los.
- CorrigirAdd your site or company name - the banner has to say who is asking.
Pré-visualização
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>O código é atualizado ao vivo, mas corrija os itens acima antes de publicá-lo.
De onde vêm as regras acima
- 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.
Este gerador produz a interface do banner e o mecanismo de armazenamento de consentimento. Não é aconselhamento jurídico e não pode, por si só, tornar um site compatível - o que realmente importa é quais rastreadores você usa, se você realmente os conectou através da barreira, e o que diz sua política de privacidade. As regras também diferem entre países e mudam com o tempo. Se o risco for alto, peça a um advogado para revisar.
Por que hospedar você mesmo em vez de usar Axeptio ou Cookiebot
As plataformas de consentimento hospedadas são ótimos produtos, mas seus planos gratuitos têm limites: ao passar de um certo número de visualizações de página por mês, o banner para de funcionar ou a cobrança começa. Elas também carregam o próprio JavaScript do próprio domínio em cada visualização de página, o que adiciona uma requisição de terceiros ao seu site em nome da privacidade, além de uma dependência que pode mudar sem o seu controle.
O trecho de código que esta ferramenta gera tem cerca de 6 KB, não tem dependências, não faz nenhuma requisição de rede por conta própria, e vive no seu próprio repositório, onde você pode ler cada linha. Não há fornecedor cuja cota possa se esgotar, e nada se comunica escondido com o exterior.
| O que o banner faz | Como |
|---|---|
| Bloqueia tags antes do consentimento | As tags de terceiros são escritas como type="text/plain" com um atributo data-cookie-category, que os navegadores se recusam a executar. Ao consentir, a tag é substituída por um elemento script real, e só então é executada. |
| Recusar é tão fácil quanto aceitar | Aceitar tudo e Rejeitar tudo são o mesmo componente de botão - mesma largura, espaçamento, peso e cor - lado a lado no primeiro nível. |
| Nada vem pré-marcado | Os interruptores de preferência gerados nunca têm o atributo checked. Nunca. |
| Silêncio não é consentimento | Fechar ou ignorar o banner não armazena nada e não libera nada. Só um clique explícito registra uma escolha. |
| Retirada do consentimento quando quiser | <ns/>.openPreferences() reabre o painel, e .reset() esquece a escolha - ligue qualquer um dos dois a um link no rodapé. |
| Lembra a resposta | localStorage, com um registro de data/hora e uma expiração que você escolhe (6 meses por padrão), para que os visitantes não sejam questionados de novo em cada página. |
Perguntas frequentes
Como crio um banner de cookies gratuito em conformidade sem uma plataforma paga?
Preencha o nome do seu site, o link da sua política de privacidade e as categorias de cookies que você realmente usa, depois copie o bloco gerado na sua página antes da tag de fechamento body. Você obtém HTML, CSS e JavaScript puros sem dependência de Axeptio, Cookiebot, Tarteaucitron ou qualquer outra plataforma de consentimento - então não há limite de páginas vistas a atingir nem script de terceiros carregando em cada página.
Ele realmente bloqueia o Google Analytics antes do consentimento, ou só esconde o banner?
Ele realmente bloqueia, mas você precisa fazer uma coisa: mudar sua tag de type="text/javascript" para type="text/plain" e adicionar data-cookie-category="analytics". Um navegador não executa um script text/plain, então a tag fica inerte na página. No momento em que o visitante concede aquela categoria, o script é clonado em um elemento script real e executa.
Por que não há opção para esconder o botão Rejeitar tudo?
Porque esse é o problema mais citado em banners não conformes. A posição da CNIL é que deve ser tão fácil recusar rastreadores quanto aceitá-los, e trata um botão "Recusar tudo" no primeiro nível, com a mesma aparência de "Aceitar tudo", como a forma clara de fazer isso. Este gerador sempre emite os dois botões com a mesma classe CSS, então são idênticos em tamanho e peso.
Isso torna meu site legalmente compatível com a LGPD/GDPR?
Não, e tome cuidado com qualquer ferramenta que afirme o contrário. Isso gera a interface do banner e o mecanismo de armazenamento do consentimento. A conformidade também depende de quais rastreadores você realmente usa, se cada um passa de fato pela barreira, do que dizem suas políticas de privacidade e cookies, e de regras que variam por país e mudam com o tempo.