TapCub 正式上线:统计、分析、客服一个平台免费用

免费工具 Cookie 弹窗生成器:同意横幅代码 + 实时预览

做外贸独立站、面向欧盟和英国访客的网站,绕不开 Cookie 同意弹窗。选好位置、颜色、文案和你真正用到的 Cookie 分类,得到一段不依赖任何库的 HTML + CSS + JS,旁边就是能点的预览;需要的话再附上一段已经和弹窗接好的 Google Consent Mode v2 代码。

  • 实时预览
  • 无依赖原生代码
  • 本地生成
生成器

先调好弹窗,再复制一整段代码

预览里跑的就是下面这段代码。试试「全部拒绝」「自定义」和右下角的「Cookie 设置」按钮,假网页下面的日志会显示弹窗发给 Google Consent Mode 的内容。

底部横条最不打扰;居中对话框会挡住页面,必须让「拒绝」和「接受」一样显眼。

文字颜色固定,保证对比度;只有按钮颜色跟随你的品牌色。

「全部接受」和「全部拒绝」共用这个颜色;按钮上的文字自动选对比度更高的黑或白。

影响按钮文字、分类名称和默认说明。面向海外访客一般选英文。

纯文本。写清可选 Cookie 用来做什么,政策链接会自动加在后面。

路径如 /cookies,或完整的 https:// 网址。还没有?用 Cookie 政策生成器 写一份。

两种都是第一方存储,只有你的网站能读到。

1–730 天。改下面的版本号也会让所有人重新选择。

新增用途或第三方服务时改一下。

实时预览

预览放在隔离的 iframe 里,拿不到 Cookie 和 localStorage,选择只记在内存中,改任何设置都会重置。不会在 tapcub.com 写任何东西。

这段弹窗做了什么

  • 已做到按钮文字对比度 5.17:1,高于 WCAG 正文 4.5:1 的下限。
  • 已做到「全部拒绝」和「全部接受」样式相同,在第一层并排显示。
  • 已做到可选分类默认不勾选;重新打开时按上次保存的选择显示。
  • 已做到使用 role="dialog",标题和说明都关联到对话框;所有控件都是真正的按钮或复选框,键盘可操作、有焦点框;按 Esc 关闭设置面板。
  • 已做到做出选择后角落会出现「Cookie 设置」按钮;任何带 data-cc-open 的元素也能重新打开设置。
  • 说明选择保存在 localStorage 的「cookie_consent」里,180 天后或版本号变化时重新询问。
  • 说明浏览器发送 GPC 信号的访客,营销类默认关闭并显示说明。加州法规(§7025)要求出售或共享个人信息的企业把这个信号当作退出请求处理。
  • 说明Consent Mode:把上面的 head 代码放在 Google 代码之前。默认全部拒绝,访客每次选择以及之后每次打开页面,弹窗都会发送 update。
  • 说明想让某段脚本等到同意后再执行:把它的 type 改成 text/plain,并加上 data-cc-category="analytics"(或 marketing / preferences)。
  • 说明预览里的链接不会跳转,选择只保存在内存中。
Consent Mode 代码:放在 <head> 最前面
<!-- Google Consent Mode v2:把这段放在 <head> 的最前面,在 Google 代码或 Tag Manager 容器代码之前。 -->
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  // 默认值:访客做出选择之前,所有可选用途都是 denied。
  gtag('consent', 'default', {
    'ad_storage': 'denied',
    'ad_user_data': 'denied',
    'ad_personalization': 'denied',
    'analytics_storage': 'denied',
    'functionality_storage': 'denied',
    'personalization_storage': 'denied',
    'security_storage': 'granted',
    'wait_for_update': 500
  });
  // 回访的访客:在任何 Google 代码运行之前,先恢复上次保存的选择。
  (function () {
    var raw = null, r = null;
    try { raw = localStorage.getItem('cookie_consent'); } catch (e) {}
    if (!raw) try { var m = document.cookie.match(/(?:^|; )cookie_consent=([^;]*)/); if (m) raw = decodeURIComponent(m[1]); } catch (e) {}
    try { r = JSON.parse(raw); } catch (e) {}
    if (!r || !r.c || !(r.e > Date.now()) || r.v !== '1') return;
    var c = r.c;
    gtag('consent', 'update', {
      'ad_storage': c.marketing ? 'granted' : 'denied',
      'ad_user_data': c.marketing ? 'granted' : 'denied',
      'ad_personalization': c.marketing ? 'granted' : 'denied',
      'analytics_storage': c.analytics ? 'granted' : 'denied',
      'functionality_storage': c.preferences ? 'granted' : 'denied',
      'personalization_storage': c.preferences ? 'granted' : 'denied'
    });
  })();
</script>

放在 Google 代码或 Tag Manager 容器代码之前。要按地区设默认值、用 GTM 或 url_passthrough,请用 Consent Mode 代码生成器,它读取的是同一份已保存的选择。

弹窗代码:放在 </body> 之前
<!-- Cookie 横幅 · 由 tapcub.com/tools/cookie-banner-generator 生成 · 无依赖。这是模板,不是法律意见,上线前请审阅。 -->
<!-- 把这一整段放在每个页面的 </body> 之前。 -->
<!-- 可选脚本写成 <script type="text/plain" data-cc-category="analytics" src="…"></script>,访客同意后才会执行。 -->
<!-- 接口:CookieConsent.get() · CookieConsent.open() · CookieConsent.reset() · CookieConsent.on(fn);任何带 data-cc-open 的元素都能重新打开设置。 -->
<style>
#cc-banner{--cc-bg:#ffffff;--cc-fg:#111827;--cc-muted:#4b5563;--cc-border:#d1d5db;--cc-panel:#f9fafb;--cc-accent:#2563eb;--cc-on-accent:#ffffff;--cc-link:#2563eb;
  position:fixed;z-index:2147483000;left:0;right:0;bottom:0;border-width:1px 0 0;box-sizing:border-box;padding:20px;background:var(--cc-bg);color:var(--cc-fg);border:1px solid var(--cc-border);
  box-shadow:0 8px 32px rgba(0,0,0,.18);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif}
#cc-banner[hidden],#cc-reopen[hidden],#cc-backdrop[hidden],#cc-banner [hidden]{display:none!important}
#cc-banner *{box-sizing:border-box}
#cc-banner:focus{outline:none}
#cc-banner .cc-inner{max-width:1080px;margin:0 auto;display:grid;gap:14px}
#cc-banner .cc-title{margin:0;font-size:17px;font-weight:700;line-height:1.3;color:var(--cc-fg)}
#cc-banner .cc-text{margin:0;color:var(--cc-muted)}
#cc-banner a{color:var(--cc-link);text-decoration:underline;text-underline-offset:2px}
#cc-banner .cc-cats{display:grid;gap:8px;margin:0;padding:0;border:0}
#cc-banner .cc-cat{display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border:1px solid var(--cc-border);border-radius:8px;background:var(--cc-panel)}
#cc-banner .cc-cat input{width:20px;height:20px;margin:2px 0 0;flex:none;accent-color:var(--cc-accent)}
#cc-banner .cc-cat b{display:block;color:var(--cc-fg)}
#cc-banner .cc-cat span{display:block;font-size:13px;color:var(--cc-muted)}
#cc-banner .cc-note{margin:0;font-size:13px;color:var(--cc-muted)}
#cc-banner .cc-actions{display:flex;flex-wrap:wrap;gap:8px}
#cc-banner .cc-btn{flex:1 1 auto;min-height:44px;padding:10px 18px;border:2px solid var(--cc-accent);border-radius:8px;background:var(--cc-accent);color:var(--cc-on-accent);font:inherit;font-weight:600;cursor:pointer}
#cc-banner .cc-btn-alt{background:transparent;color:var(--cc-fg);border-color:var(--cc-border)}
#cc-banner .cc-btn:focus-visible,#cc-reopen:focus-visible,#cc-banner a:focus-visible,#cc-banner input:focus-visible{outline:3px solid var(--cc-link);outline-offset:2px}
#cc-backdrop{position:fixed;inset:0;z-index:2147482999;background:rgba(17,24,39,.55)}
#cc-reopen{position:fixed;z-index:2147482998;left:16px;bottom:16px;min-height:40px;padding:8px 14px;border:1px solid #d1d5db;border-radius:999px;background:#ffffff;color:#111827;font:600 13px/1.2 system-ui,sans-serif;cursor:pointer;box-shadow:0 2px 10px rgba(0,0,0,.15)}
#cc-reopen:focus-visible{outline:3px solid #2563eb;outline-offset:2px}
@media (min-width:720px){#cc-banner.cc-bar .cc-inner{grid-template-columns:1fr auto;align-items:center}#cc-banner.cc-bar .cc-prefs,#cc-banner.cc-bar .cc-actions-wide{grid-column:1/-1}#cc-banner .cc-btn{flex:0 1 auto}}
</style>
<div id="cc-banner" class="cc-bar" role="dialog" aria-modal="false" aria-labelledby="cc-title" aria-describedby="cc-desc" tabindex="-1" lang="en" hidden>
  <div class="cc-inner">
    <div>
      <h2 class="cc-title" id="cc-title">We use cookies</h2>
      <p class="cc-text" id="cc-desc">We use strictly necessary cookies to run this site. With your consent we also use cookies for analytics and marketing. You can change your choice at any time in “Cookie settings”. <a href="/cookies">Cookie policy</a></p>
    </div>
    <div class="cc-prefs" id="cc-prefs" hidden>
      <fieldset class="cc-cats">
      <legend class="cc-note">Cookie categories</legend>
      <div class="cc-cat"><input type="checkbox" id="cc-cat-necessary" checked disabled><label for="cc-cat-necessary"><b>Strictly necessary</b><span>Always on. Needed for the site to work, for example security and remembering this choice.</span></label></div>
      <div class="cc-cat"><input type="checkbox" id="cc-cat-analytics" data-cc-cat="analytics"><label for="cc-cat-analytics"><b>Analytics</b><span>Helps us understand how the site is used, for example which pages are visited.</span></label></div>
      <div class="cc-cat"><input type="checkbox" id="cc-cat-marketing" data-cc-cat="marketing"><label for="cc-cat-marketing"><b>Marketing</b><span>Used to measure ad campaigns and show more relevant ads.</span></label></div>
      </fieldset>
      <p class="cc-note" data-cc-gpc hidden>Opt-out preference signal honored: your browser sent Global Privacy Control, so marketing stays off unless you switch it on here.</p>
    </div>
    <div class="cc-actions">
      <button type="button" class="cc-btn" data-cc="reject">Reject all</button>
      <button type="button" class="cc-btn" data-cc="accept">Accept all</button>
      <button type="button" class="cc-btn cc-btn-alt" data-cc="customize" aria-expanded="false" aria-controls="cc-prefs">Customize</button>
      <button type="button" class="cc-btn cc-btn-alt" data-cc="save" hidden>Save choices</button>
    </div>
  </div>
</div>
<button type="button" id="cc-reopen" data-cc-open hidden>Cookie settings</button>
<script>
(function () {
  'use strict';
  // 设置:在这里改,不用改下面的代码。
  var KEY = "cookie_consent"; // localStorage 键名 / Cookie 名
  var VERSION = "1"; // 用途变了就改它:所有访客会被重新询问
  var DAYS = 180; // 多少天后重新询问
  var STORAGE = "local"; // 'local' = localStorage,'cookie' = 第一方 Cookie
  var CATEGORIES = ["analytics","marketing"];
  var CONSENT_MODE = true; // 发送 gtag('consent', 'update', …)
  var TAPCUB = false; // 同步给 TapCub 统计脚本(只在识别模式下有用)
  var HONOR_GPC = true; // 收到全球隐私控制(GPC)信号时营销类保持关闭
  var MODAL = false;

  var root = document.getElementById('cc-banner');
  if (!root) return;
  var backdrop = document.getElementById('cc-backdrop');
  var reopenBtn = document.getElementById('cc-reopen');
  var prefs = document.getElementById('cc-prefs');
  var memory = null, lastFocus = null, firstRun = false;
  var gpc = HONOR_GPC && navigator.globalPrivacyControl === true && CATEGORIES.indexOf('marketing') > -1;
  // 把横幅放到 Tab 顺序的最前面
  document.body.insertBefore(root, document.body.firstChild);
  if (backdrop) document.body.insertBefore(backdrop, root);

  // ---- 存储:浏览器禁止存储时退回内存
  function read() {
    var raw = null;
    try {
      if (STORAGE === 'local') raw = localStorage.getItem(KEY);
      else { var m = document.cookie.match(new RegExp('(?:^|; )' + KEY + '=([^;]*)')); if (m) raw = decodeURIComponent(m[1]); }
    } catch (e) {}
    if (!raw) return memory;
    try { return JSON.parse(raw); } catch (e) { return null; }
  }
  function write(rec) {
    memory = rec;
    var s = rec ? JSON.stringify(rec) : null;
    try {
      if (STORAGE === 'local') { if (s) localStorage.setItem(KEY, s); else localStorage.removeItem(KEY); }
      else document.cookie = KEY + '=' + (s ? encodeURIComponent(s) : '') + '; Max-Age=' + (s ? DAYS * 86400 : 0) + '; Path=/; SameSite=Lax' + (location.protocol === 'https:' ? '; Secure' : '');
    } catch (e) {}
  }
  function valid(r) { return !!(r && r.c && r.v === VERSION && r.e > Date.now()); }

  // ---- 做出选择之后
  function apply(c, userAction) {
    if (CONSENT_MODE) {
      window.dataLayer = window.dataLayer || [];
      if (typeof window.gtag !== 'function') window.gtag = function () { window.dataLayer.push(arguments); };
      window.gtag('consent', 'update', {
        'ad_storage': c.marketing ? 'granted' : 'denied',
        'ad_user_data': c.marketing ? 'granted' : 'denied',
        'ad_personalization': c.marketing ? 'granted' : 'denied',
        'analytics_storage': c.analytics ? 'granted' : 'denied',
        'functionality_storage': c.preferences ? 'granted' : 'denied',
        'personalization_storage': c.preferences ? 'granted' : 'denied'
      });
      window.dataLayer.push({ event: 'cookie_consent_update' });
    }
    // 执行已获同意的 <script type="text/plain" data-cc-category="…">
    var blocked = document.querySelectorAll('script[type="text/plain"][data-cc-category]');
    for (var i = 0; i < blocked.length; i++) {
      var old = blocked[i];
      if (!c[old.getAttribute('data-cc-category')]) continue;
      var s = document.createElement('script');
      for (var k = 0; k < old.attributes.length; k++) {
        var a = old.attributes[k];
        if (a.name !== 'type' && a.name !== 'data-cc-category') s.setAttribute(a.name, a.value);
      }
      s.text = old.text;
      old.parentNode.replaceChild(s, old);
    }
    var ev;
    try { ev = new CustomEvent('cookieconsent', { detail: c }); } catch (e) { ev = document.createEvent('CustomEvent'); ev.initCustomEvent('cookieconsent', false, false, c); }
    document.dispatchEvent(ev);
  }

  function choice(all) {
    var c = {};
    for (var i = 0; i < CATEGORIES.length; i++) {
      var k = CATEGORIES[i];
      if (all === null) { var box = document.getElementById('cc-cat-' + k); c[k] = !!(box && box.checked); }
      else c[k] = all && !(gpc && k === 'marketing');
    }
    return c;
  }
  function save(c) {
    var now = Date.now();
    write({ v: VERSION, t: now, e: now + DAYS * 864e5, c: c });
    apply(c, true);
    close();
  }

  // ---- 打开 / 关闭和焦点
  function focusables() { return root.querySelectorAll('button:not([hidden]):not([disabled]), a[href], input:not([disabled])'); }
  function showPrefs(on) {
    if (!prefs) return;
    prefs.hidden = !on;
    var cus = root.querySelector('[data-cc="customize"]');
    var sav = root.querySelector('[data-cc="save"]');
    if (cus) { cus.hidden = on; cus.setAttribute('aria-expanded', String(on)); }
    if (sav) sav.hidden = !on;
    if (on) { var first = prefs.querySelector('input:not([disabled])'); if (first) first.focus(); }
  }
  function open(withPrefs, byUser) {
    var r = read(), saved = valid(r) ? r.c : {};
    for (var i = 0; i < CATEGORIES.length; i++) {
      var box = document.getElementById('cc-cat-' + CATEGORIES[i]);
      if (box) box.checked = saved[CATEGORIES[i]] === true; // 不预先勾选
    }
    var g = root.querySelector('[data-cc-gpc]'); if (g) g.hidden = !gpc;
    lastFocus = byUser ? document.activeElement : null;
    root.hidden = false;
    if (backdrop) backdrop.hidden = false;
    if (reopenBtn) reopenBtn.hidden = true;
    showPrefs(!!withPrefs);
    if (!MODAL) document.documentElement.style.scrollPaddingBottom = root.offsetHeight + 'px';
    if ((MODAL || byUser) && !withPrefs) root.focus();
  }
  function close() {
    root.hidden = true;
    firstRun = false;
    if (backdrop) backdrop.hidden = true;
    document.documentElement.style.scrollPaddingBottom = '';
    if (reopenBtn) reopenBtn.hidden = false;
    var to = lastFocus && document.contains(lastFocus) ? lastFocus : reopenBtn;
    if (to && to.focus) to.focus();
  }

  root.addEventListener('click', function (e) {
    var b = e.target.closest ? e.target.closest('[data-cc]') : null;
    if (!b) return;
    var act = b.getAttribute('data-cc');
    if (act === 'accept') save(choice(true));
    else if (act === 'reject') save(choice(false));
    else if (act === 'save') save(choice(null));
    else if (act === 'customize') showPrefs(true);
  });
  root.addEventListener('keydown', function (e) {
    if (e.key === 'Escape') {
      if (prefs && !prefs.hidden) { showPrefs(false); var cus = root.querySelector('[data-cc="customize"]'); if (cus) cus.focus(); }
      else if (!firstRun) close();
      return;
    }
    if (e.key !== 'Tab' || !MODAL) return;
    var f = focusables(); if (!f.length) return;
    var first = f[0], last = f[f.length - 1];
    if (e.shiftKey && (document.activeElement === first || document.activeElement === root)) { e.preventDefault(); last.focus(); }
    else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
  });
  document.addEventListener('click', function (e) {
    var t = e.target.closest ? e.target.closest('[data-cc-open]') : null;
    if (t) { e.preventDefault(); open(true, true); }
  });

  window.CookieConsent = {
    get: function () { var r = read(); return valid(r) ? r.c : null; },
    open: function () { open(true, true); },
    reset: function () { write(null); firstRun = true; open(false, false); },
    on: function (fn) { document.addEventListener('cookieconsent', function (e) { fn(e.detail); }); }
  };

  var rec = read();
  if (valid(rec)) { apply(rec.c, false); if (reopenBtn) reopenBtn.hidden = false; }
  else { if (rec) write(null); firstRun = true; open(false, false); }
})();
</script>

每个页面放一整段。设置项都在脚本开头。

全部在你的浏览器里计算。这里输入的任何内容都不会上传,TapCub 也不会保存。

分享这组输入:数值写在链接的 # 后面,不会发到服务器。

版本 v1.0更新于 更新记录

看懂结果

生成的代码由哪几部分组成

一段弹窗代码,加一段可选的 Google 代码片段,不从任何第三方服务器加载东西。

弹窗代码

一个只作用于 #cc-banner 的 <style>、弹窗的 HTML 和一段脚本。存储键、版本号、天数、分类这些设置都写在脚本开头,改设置不用动逻辑。

第一层

标题、说明、政策链接,然后是「全部拒绝」「全部接受」「自定义」。拒绝和接受颜色、大小、位置完全对等;「自定义」展开分类列表,「必要」一项锁定开启。

Consent Mode 代码

放在 <head> 最前面:先把所有可选的 consent type 设为 denied,并带 wait_for_update: 500,再恢复访客上次保存的选择,让 Google 代码运行前就拿到结果。

检查清单

按钮文字对比度、缺少政策链接、重新询问间隔过长,以及每个选项的作用。它只说明这段代码本身;网站要不要弹窗、要哪些分类,取决于网站实际加载了什么。

弹窗只负责记录选择,管不了它控制不到的脚本:直接写死在页面里的统计、广告代码照样会执行,除非用 type="text/plain" 拦住,或者通过 Google Consent Mode / Tag Manager 加载。

怎么用

四步

大约十分钟,大部分时间花在第一步。

  1. 1

    先盘点网站存了什么

    用无痕窗口打开网站,按 F12 打开开发者工具的「应用」(Application)面板,记下每个 Cookie 和 localStorage 项是哪个工具写的。只勾选你真的找到的分类。

  2. 2

    调样式、写文案

    选位置和颜色,看一眼对比度提示;说明里写清每类可选 Cookie 的用途,并链到 Cookie 政策。面向欧美访客的独立站,弹窗语言选英文。

  3. 3

    贴代码

    弹窗代码放在每个页面的 </body> 之前;Consent Mode 代码放在 <head> 最前面。Shopify、WordPress 等建站系统一般在主题模板里加。可选脚本改成 type="text/plain" 并加 data-cc-category。

  4. 4

    三条路径都测一遍

    无痕窗口里:点「全部拒绝」,确认存储面板仍然是干净的;再点「全部接受」;再重新打开设置撤回。开了 Consent Mode 的,用 Google Tag Assistant 看默认值和 update 是否生效。

默认设置背后的规则

弹窗为什么这样设计

每个默认做法都对应一条公开的规则或监管指引:

  • 在用户设备上存储或读取信息,除非是提供用户所要求服务所必需的,都要先征得同意:欧盟 ePrivacy 指令第 5(3) 条,localStorage 同样适用
  • 同意必须是明确的主动行为,预先勾选的选项不算:欧盟法院 C-673/17 Planet49 案(2019-10-01)、GDPR 序言第 32 条
  • 撤回同意要和给出同意一样容易:GDPR 第 7(3) 条,所以要有重新打开的按钮和 data-cc-open
  • 拒绝要和接受一样容易:法国 CNIL 2020-091 号指南和 2020-092 号建议(2020 年 9 月);EDPB Cookie 横幅工作组报告(2023 年 1 月)把第一层没有拒绝按钮、预先勾选、误导性的按钮颜色和对比度列为重点审查的做法
  • 国内:《个人信息保护法》第 14 条要求同意在充分知情的前提下自愿、明确作出,第 15 条要求提供便捷的撤回方式;2019 年《App 违法违规收集使用个人信息行为认定方法》把默认勾选同意列为「未经用户同意」的情形
弹窗行为和出处
要求生成代码里的做法出处
拒绝和接受一样容易同样式、同一层、并排CNIL 2020-092;EDPB 工作组报告
不预先勾选可选分类默认不勾C-673/17 Planet49;工信部等 2019 认定方法
同意前不运行可选内容被拦的脚本保持 text/plain;Consent Mode 默认 deniedePrivacy 第 5(3) 条
方便撤回悬浮按钮、data-cc-open、CookieConsent.open()GDPR 第 7(3) 条;个保法第 15 条
看得清、键盘能用按钮文字 ≥ 4.5:1、焦点可见、对话框有标题WCAG 2.2 SC 1.4.3、2.1.1、2.4.7
退出偏好信号GPC 时营销类保持关闭并说明加州法规 tit. 11 §7025
例子

三个网站,三种弹窗

勾什么取决于网站加载了什么。三个例子用的都是同一个生成器。

弹窗配置示例
网站加载了什么设置结果
外贸 B2B 独立站GA4勾统计分析;开 Consent Mode拒绝 / 接受 / 自定义;GA4 在访客接受前按 denied 运行
跨境电商独立站GA4、Google Ads、Meta Pixel统计分析 + 营销;开 Consent Mode;像素代码加 data-cc-category="marketing"像素只在同意后加载;Google 代码收到 update
只用 TapCub 的官网TapCub 默认模式 + 在线客服不勾可选分类一条只有「知道了」的提示,或经法务确认后不弹窗:两段脚本在访客操作之前都不在设备上存东西

第三行是 TapCub 自己的情况:统计脚本默认不写 Cookie、不写浏览器存储;客服插件只在访客开始对话后写一个会话令牌(见 TapCub 的 Cookie 说明页)。

常见错误

Cookie 弹窗容易出问题的地方

多数问题不在弹窗本身,而在它周围跑的那些代码。

选择之前代码就跑了

页面里的 GA、像素代码已经执行,再弹窗征求同意就晚了。把这些脚本拦住,或用 Consent Mode 的默认拒绝。

拒绝藏在第二层

大大的「接受」旁边只有一个小小的「设置」链接,正是监管最先查的做法。拒绝要放在第一层、同样显眼。

选完就再也找不到

弹窗不再出现,访客就没法撤回同意。保留悬浮按钮,或在页脚放一个带 data-cc-open 的链接。

分类和实际不符

没有广告代码却列「营销」,或把统计 Cookie 说成「必要」,都会让弹窗失实。先盘点,再勾选。

常见问题

Cookie 弹窗常见问题

还有其他问题?

直接和 TapCub 团队聊聊——工作日通常几小时内回复。

网站一定要做 Cookie 弹窗吗?

面向欧盟、英国访客时,在设备上存储或读取非必要信息之前要先征得同意(ePrivacy 指令第 5(3) 条及各国法律)。只用登录会话、购物车这类必要存储的,这些项不需要弹窗;统计和广告 Cookie 通常需要。国内法律没有专门的 Cookie 条款,但用 Cookie 等收集个人信息时要遵守《个人信息保护法》的告知和同意规定。本页是模板,不是法律意见,具体请咨询律师。

做了 Cookie 弹窗就符合 GDPR 了吗?

不是。弹窗只记录选择,还需要:脚本真的按选择执行、政策里写清每个用途、依赖同意时保留同意记录、提供撤回方式。生成的代码负责弹窗这一块,并能把选择接到 Google Consent Mode 和被拦截的脚本上。

「拒绝」按钮必须和「接受」一样吗?

法国 CNIL 2020 年的建议给出的做法是「全部拒绝」和「全部接受」外观相同;EDPB 的 Cookie 横幅工作组也把第一层没有拒绝按钮、误导性的颜色和对比度列为审查重点。稳妥的做法就是本工具用的:两个按钮同样式、都在第一层。

Cookie 同意的选择应该保存多久?

GDPR 没有给出固定天数。选一个能在 Cookie 政策里说明理由的期限,到期再问一次,并在政策里写清选择保存多久。填好天数;每次新增用途时改一下版本号,所有人都会重新选择。

怎么在访客同意前不加载 Google Analytics?

两种做法。打开 Consent Mode,把 head 代码放在 Google 代码前面:GA4 以 analytics_storage = denied 启动,访客接受后再更新。或者整段拦住:把 <script> 改成 <script type="text/plain" data-cc-category="analytics">,弹窗只在同意后执行它。

访客以后能改主意吗?

能。做出选择后,角落里会留一个「Cookie 设置」按钮;任何带 data-cc-open 的元素或调用 CookieConsent.open() 都会重新打开分类设置,并显示上次保存的选择。

国内的 App 隐私弹窗、小程序隐私授权也能用这个吗?

不能直接用。App 首次启动的隐私政策弹窗、微信小程序的隐私授权弹窗是各自平台的机制,有专门的接口和审核要求。本工具生成的是网页上的 Cookie 同意弹窗,主要用于面向欧盟、英国、美国加州访客的网站。

预览会在 tapcub.com 写 Cookie 吗?

不会。预览放在隔离的 iframe 里,拿不到 Cookie 和 localStorage,生成的代码会退回内存保存;改设置或离开页面后选择就消失了。

更新记录

更新记录

这个工具的新功能、修复和数据更新,最新的在前。

  1. · v1.0

    首次上线首次上线。

这个工具用到的数据

  • Google Consent Mode 官方文档 — 核对于 2026-11-10,每 6 个月复核(2027-05-10 前复核)

缺功能或发现错误?告诉我们,常被提到的会排进下次更新。 全部工具更新

看清数据,找到增长。

每一次点击,都有数据依据。装好一行代码,1 分钟看到第一批数据。

无需信用卡 · 统计与客服都有免费版 · 统计无 Cookie