EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Web / แบนเนอร์คุกกี้ที่กั้นได้จริง โหลด GTM กับ GA4 หลังกดยอมรับเท่านั้น
Web

แบนเนอร์คุกกี้ที่กั้นได้จริง โหลด GTM กับ GA4 หลังกดยอมรับเท่านั้น

โดย Ton-Or ·18 ต.ค. 2026·อ่าน 9 นาที
แบนเนอร์คุกกี้ที่กั้นได้จริง โหลด GTM กับ GA4 หลังกดยอมรับเท่านั้น

แบนเนอร์คุกกี้ที่ขึ้นมาตอนโหลดหน้าเสร็จ ทั้งที่สคริปต์เก็บข้อมูลวิ่งไปหมดแล้วตั้งแต่บรรทัดแรก มีค่าเท่ากับป้ายประกาศ ไม่ใช่ระบบขอความยินยอม ความต่างของสองอย่างนี้อยู่ที่ลำดับการโหลดไม่กี่บรรทัด แต่เป็นไม่กี่บรรทัดที่ตัดสินว่าเว็บเราทำตามที่เขียนไว้ในหน้านโยบายหรือเปล่า

สรุปใน 1 นาที

อาการ/ปัญหา
แบนเนอร์คุกกี้ขึ้นสวยแต่ไม่กั้นอะไร เพราะ GTM กับ GA4 โหลดไปตั้งแต่บรรทัดแรก
ใครควรอ่าน
คนทำเว็บที่ใช้ GTM, GA4 หรือ AdSense และต้องขอความยินยอมจริง
ทางแก้แบบสั้น
โหลด GTM container เมื่อกดยอมรับเท่านั้น วาง GA4 เป็นแท็กข้างใน GTM ซ่อนช่อง AdSense ไว้ก่อน แล้วตรวจว่า request ปลายทางเป็นศูนย์ก่อนกด
ข้ามไปขั้นตอน

พอดีผมทำเว็บที่มีทั้ง Google Tag Manager, GA4 และ AdSense อยู่ด้วยกัน แล้วต้องขึ้นแบนเนอร์คุกกี้ตามที่เขียนไว้ในหน้านโยบาย ระหว่างไล่ดูของที่ทำไว้เดิมก็เจอว่าแบนเนอร์มันขึ้นจริง ปุ่มกดได้จริง เก็บค่าไว้จริง แต่ถ้าเปิดแท็บ Network ดูตอนที่แบนเนอร์ยังค้างอยู่บนจอ จะเห็นว่า container ของ GTM โหลดไปเรียบร้อยแล้ว GA4 ยิง collect ไปแล้วหนึ่งนัด ทั้งที่ผู้ใช้ยังไม่ได้แตะปุ่มอะไรเลย ผมเลยรื้อใหม่ทั้งชุด

ค่า ID ทุกตัวในบทความเป็นค่าตัวอย่าง ให้เปลี่ยนเป็นของตัวเองก่อนใช้งาน ส่วนโครงกับลำดับการโหลดคือของจริงที่ใช้อยู่

ปัญหา: แบนเนอร์ที่ไม่ได้กั้นอะไรเลย

โค้ดที่เจอบ่อยที่สุดคือแบบนี้ คือเอาสคริปต์มาตรฐานที่ Google ให้มาวางไว้บนสุดของ <head> ตามคู่มือ แล้วค่อยเขียนแบนเนอร์แยกไว้อีกไฟล์

<!-- แบบที่เจอบ่อย และไม่ได้กั้นอะไรเลย -->
<head>
  <!-- GTM โหลดทันทีที่หน้าเริ่มพาร์ส -->
  <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});
  var f=d.getElementsByTagName(s)[0],j=d.createElement(s);j.async=true;
  j.src='https://www.googletagmanager.com/gtm.js?id='+i;f.parentNode.insertBefore(j,f);
  })(window,document,'script','dataLayer','GTM-XXXXXXX');</script>

  <!-- GA4 โหลดตรงอีกตัว ซ้ำกับที่ตั้งไว้ใน GTM -->
  <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
</head>
<body>
  <div id="cookie-bar">เว็บนี้ใช้คุกกี้ <button>ยอมรับ</button></div>
</body>

ตรงนี้มีปัญหาสองชั้นซ้อนกัน ชั้นแรกคือความยินยอมถูกขอหลังจากเก็บข้อมูลไปแล้ว ซึ่งขัดกับหลักที่เรียกว่า prior consent ตรง ๆ ชั้นที่สองคือ GA4 ถูกโหลดสองทาง ทั้งจาก tag ที่ตั้งไว้ใน GTM และจากบรรทัด gtag/js ที่วางไว้เอง ผลคือยอดผู้เข้าชมถูกนับซ้ำโดยที่ไม่มีใครสังเกต เพราะตัวเลขมันดูสวยขึ้น

แบนเนอร์ไม่ได้ทำให้เว็บทำตามนโยบาย โค้ดต่างหากที่ทำ

สิ่งที่ตรวจสอบได้จริงมีอย่างเดียวคือ จำนวน request ที่ออกไปหาปลายทาง ก่อนที่ผู้ใช้จะกดยอมรับ ถ้าเลขนั้นไม่ใช่ศูนย์ แบนเนอร์สวยแค่ไหน ข้อความยาวแค่ไหน ก็ไม่มีความหมาย ตัวเลขนี้ใครก็เปิด DevTools ดูได้ในสิบวินาที รวมถึงคนที่ตั้งใจจะมาหาเรื่องเราด้วย

เลือกทางก่อนลงมือ: กั้นทั้ง container หรือใช้ consent mode

มีสองทางที่ใช้กันจริงและทั้งคู่ใช้ได้ ต่างกันที่ระดับความเข้ม

ทางแรกคือ consent mode คือยังโหลด GTM ตามปกติ แต่ประกาศสถานะเริ่มต้นเป็นปฏิเสธไว้ก่อน แล้วอัปเดตเมื่อผู้ใช้กดยอมรับ ข้อดีคือได้ข้อมูลแบบไม่ระบุตัวตนตั้งแต่ก่อนกดยอมรับ และแท็กหลายตัวรองรับอยู่แล้ว

// ทางที่หนึ่ง: consent mode — ยังโหลด GTM แต่ประกาศสถานะเริ่มต้นไว้ก่อน
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('consent', 'default', {
  ad_storage: 'denied',
  ad_user_data: 'denied',
  ad_personalization: 'denied',
  analytics_storage: 'denied',
  wait_for_update: 500,
});
// ...เมื่อผู้ใช้กดยอมรับ
gtag('consent', 'update', { ad_storage: 'granted', analytics_storage: 'granted' });

ทางที่สองคือไม่โหลด container เลยจนกว่าจะกดยอมรับ ข้อเสียคือเสียข้อมูลช่วงก่อนกด และเสียความสามารถบางอย่างของ consent mode ไป ข้อดีคือมันตรวจสอบได้ด้วยตาเปล่า เปิด Network ดู ถ้าไม่มี request ออกไปเลยก็จบ ไม่ต้องไปเชื่อว่าสคริปต์ปลายทางเคารพธงที่เราตั้งไว้จริงหรือเปล่า

ส่วนตัวผมเลือกทางที่สองเป็นหลักครับ เพราะเว็บที่ผมดูแลไม่ได้ทำการตลาดแบบยิงแอดที่ต้องพึ่งข้อมูลช่วงก่อนยินยอม การเสียข้อมูลไปนิดหน่อยแลกกับข้อพิสูจน์ที่ชัดเจนว่าไม่มีอะไรออกไปนั้นคุ้มกว่ามาก ใครที่ต้องพึ่งข้อมูลแอดจริงจังก็เลือกทางแรกได้ตามสะดวก แต่ต้องยอมรับว่าต้องไล่ตรวจแต่ละแท็กเองว่าเคารพธงหรือเปล่า

สามช่วงเวลา เทียบแบบเดิมกับแบบกั้นด้วยการกดยอมรับหน้าเริ่มโหลดแบนเนอร์ค้างบนจอหลังกดยอมรับแบบเดิมGTM โหลดทันทีgtag/js โหลดซ้ำอีกตัวGA4 ยิง collect แล้วผู้ใช้ยังไม่แตะปุ่มยอดผู้เข้าชมนับซ้ำGA4 มาสองทางแบบกั้นไม่มี request ออกสถานะ nullโชว์แบนเนอร์request = 0โหลด gtm.js 1 ครั้งGA4 อยู่ใน GTM ตัวเดียวตัวตรวจ: ก่อนกด 0 request และ gtm.js หลังโหลดซ้ำ = 1 ครั้ง
แถวบนคือโค้ดแบบเดิม แถวล่างคือโครงที่โหลด container เมื่อกดยอมรับเท่านั้น
consent mode

โหลดก่อน แล้วบอกว่าห้ามเก็บ

  • ได้ข้อมูลแบบไม่ระบุตัวตนก่อนกด
  • แท็กในระบบเดียวกันรองรับเอง
  • พิสูจน์ยาก ต้องเชื่อปลายทาง
  • เหมาะกับเว็บที่พึ่งข้อมูลแอด
กั้นทั้ง container

ไม่โหลดจนกว่าจะกด

  • ก่อนกดไม่มี request ออกเลย
  • พิสูจน์ได้ด้วย Network หรือสคริปต์
  • เสียข้อมูลช่วงก่อนกดทั้งหมด
  • เหมาะกับเว็บเนื้อหาที่รัน AdSense

สามสถานะที่ต้องออกแบบให้ครบ

คนมักออกแบบแค่สองสถานะคือยังไม่กด กับกดยอมรับแล้ว แล้วลืมสถานะที่สาม คือ กดปฏิเสธไปแล้ว ซึ่งต้องไม่ถามซ้ำทุกครั้งที่เปลี่ยนหน้า ไม่งั้นผู้ใช้จะรำคาญจนไปกดยอมรับเพื่อให้มันหายไป ซึ่งนั่นไม่ใช่ความยินยอมที่ได้มาโดยสมัครใจ

สามสถานะยังไม่เลือกค่าเป็น null->โชว์แบนเนอร์ไม่โหลดอะไร->กดยอมรับโหลด GTM หนึ่งครั้ง
เส้นทางปฏิเสธกดปฏิเสธบันทึกว่า declined->ซ่อนแบนเนอร์ถาวรไม่ถามซ้ำ->ซ่อนช่องโฆษณาเว็บใช้งานได้ปกติ->เหลือปุ่มที่ท้ายเว็บเปลี่ยนใจได้เอง

แบนเนอร์: ปุ่มสองปุ่มต้องเท่ากัน

เรื่องหน้าตามีข้อเดียวที่ผมถือว่าสำคัญคือปุ่มยอมรับกับปฏิเสธต้องมีน้ำหนักเท่ากัน ขนาดเท่ากัน อ่านง่ายเท่ากัน การทำปุ่มปฏิเสธให้เป็นตัวหนังสือจาง ๆ เล็ก ๆ มุมล่าง คือการออกแบบให้คนกดผิด และมันทำให้ความยินยอมที่ได้มาไม่มีน้ำหนัก

<!-- วางท้าย body — ไม่ต้องรีบ ไม่ต้องบล็อกการเรนเดอร์ -->
<div id="ck-bar" class="ck-bar" hidden role="dialog" aria-label="การใช้คุกกี้">
  <p class="ck-txt">เว็บนี้ใช้คุกกี้เพื่อวัดผลการใช้งานและแสดงโฆษณา
     ถ้ายังไม่ยอมรับ เว็บยังใช้งานได้ตามปกติทุกส่วน
     <a href="/privacy">อ่านนโยบายความเป็นส่วนตัว</a></p>
  <div class="ck-act">
    <button type="button" id="ck-no"  class="ck-btn">ปฏิเสธ</button>
    <button type="button" id="ck-yes" class="ck-btn ck-primary">ยอมรับ</button>
  </div>
</div>
.ck-bar{position:fixed;left:0;right:0;bottom:0;z-index:9999;display:flex;gap:16px;
        align-items:center;flex-wrap:wrap;padding:16px 20px;
        background:#17130e;color:#f1ece0;font-size:.92rem;line-height:1.7}
.ck-txt{margin:0;flex:1 1 320px}
.ck-txt a{color:#f3713f}
.ck-act{display:flex;gap:10px;flex:0 0 auto}
/* ปุ่มสองปุ่มขนาดเท่ากัน อ่านง่ายเท่ากัน */
.ck-btn{min-width:128px;padding:11px 18px;border:1px solid #f1ece0;border-radius:2px;
        background:transparent;color:#f1ece0;font-size:.92rem;cursor:pointer}
.ck-primary{background:#e5330b;border-color:#e5330b;color:#fff}
@media (max-width:480px){.ck-act{width:100%}.ck-btn{flex:1 1 0}}

แกนกลาง: ตัวโหลด GTM ที่เรียกซ้ำไม่ได้

หัวใจอยู่ที่ตรงนี้ครับ สคริปต์ของ GTM ต้องอยู่ในฟังก์ชัน และฟังก์ชันนั้นต้องถูกเรียกจากทางเดียวคือเมื่อสถานะเป็นยอมรับเท่านั้น พร้อมตัวกันเรียกซ้ำ เพราะถ้าโหลด container สองรอบ dataLayer จะถูก process ซ้ำและยอดจะเพี้ยน

// consent.js — ทั้งหมดของการกั้นอยู่ในไฟล์นี้ไฟล์เดียว
(function () {
  'use strict';

  var KEY = 'ck_consent';          // accepted | declined | (ไม่มีค่า = ยังไม่เลือก)
  var GTM_ID = 'GTM-XXXXXXX';      // เปลี่ยนเป็นของตัวเอง
  var loaded = false;

  function readState() {
    try { return localStorage.getItem(KEY); } catch (e) { return null; }
  }
  function writeState(v) {
    try { localStorage.setItem(KEY, v); } catch (e) { /* โหมดส่วนตัว เขียนไม่ได้ ไม่ต้องพัง */ }
  }

  function loadGTM() {
    if (loaded) return;            // กันเรียกซ้ำ สำคัญมาก
    loaded = true;
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({ 'gtm.start': Date.now(), event: 'gtm.js' });
    var j = document.createElement('script');
    j.async = true;
    j.src = 'https://www.googletagmanager.com/gtm.js?id=' + GTM_ID;
    document.head.appendChild(j);
  }

  function accept() {
    writeState('accepted');
    hideBar();
    document.documentElement.classList.remove('ck-noads');
    loadGTM();
    showAds();
  }

  function decline() {
    writeState('declined');
    hideBar();                     // ไม่โหลดอะไรทั้งนั้น ช่องโฆษณายังซ่อนอยู่
  }

  function hideBar() { var b = document.getElementById('ck-bar'); if (b) b.hidden = true; }
  function showBar() {
    var b = document.getElementById('ck-bar');
    if (!b) return;
    b.hidden = false;
    document.getElementById('ck-yes').addEventListener('click', accept);
    document.getElementById('ck-no').addEventListener('click', decline);
  }

  var state = readState();
  if (state !== 'accepted') document.documentElement.classList.add('ck-noads');
  if (state === 'accepted') { loadGTM(); showAds(); }
  else if (state === null)  { document.addEventListener('DOMContentLoaded', showBar); }

  window.ckManage = function () {  // ให้ปุ่มท้ายเว็บเรียกเพื่อเปลี่ยนใจ
    try { localStorage.removeItem(KEY); } catch (e) {}
    location.reload();
  };
})();
localStorage เขียนไม่ได้ในบางโหมด ต้องครอบ try ทุกครั้ง

ในหน้าต่างส่วนตัวของบางเบราว์เซอร์ หรือเมื่อผู้ใช้ตั้งค่าบล็อกข้อมูลเว็บไซต์ไว้ คำสั่ง localStorage.setItem จะโยน error ออกมา ไม่ได้คืนค่า false ถ้าไม่ครอบ try ไว้ ปุ่มยอมรับจะกดแล้วไม่มีอะไรเกิดขึ้นเลยเพราะสคริปต์ตายก่อนถึงบรรทัดโหลด GTM และเส้นทางที่จำค่าไม่ได้ต้องยังใช้งานได้ คือถามใหม่รอบหน้า ไม่ใช่หน้าพัง

GA4 ต้องเป็นแท็กข้างใน GTM และตั้งใจไม่ใส่ noscript

ถ้าจะใช้ GTM แล้ว ให้ตั้ง GA4 เป็น tag ข้างใน container ไปเลย อย่าไปวาง gtag/js คู่ไว้ในหน้าอีกตัว เพราะมันคือการยิงสองทางไปหาปลายทางเดียวกัน ยอดจะเกินจริงและหาสาเหตุยากมากเวลาตัวเลขไม่ตรงกับความรู้สึก

// ถ้ามีอีเวนต์ของเราเอง ให้ push เข้า dataLayer แล้วให้ GTM เป็นคนตัดสินใจส่งต่อ
function track(name, params) {
  if (!window.dataLayer) return;              // ยังไม่ยอมรับ ก็ไม่มี dataLayer ให้ push
  window.dataLayer.push(Object.assign({ event: name }, params || {}));
}
// ตัวอย่าง: track('form_submit', { form_id: 'contact' });
// GA4 config อยู่ใน GTM ตัวเดียว ไม่มี gtag/js ในหน้าอีกแล้ว

อีกเรื่องที่ผมตั้งใจไม่ใส่คือ noscript iframe ที่คู่มือของ GTM ให้มาวางไว้ต้น <body>

<!-- ตัวนี้ผมตั้งใจไม่ใส่ เพราะมันทำงานนอกเหนือการควบคุมของ JS ทั้งหมด -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>

เหตุผลตรงไปตรงมาครับ ตัวกั้นทั้งหมดของเราเป็น JavaScript ถ้าผู้ใช้ปิด JavaScript แบนเนอร์ก็ไม่ขึ้น ปุ่มก็กดไม่ได้ แต่ iframe ตัวนี้ยังโหลดตามปกติ กลายเป็นว่าคนที่ระวังตัวที่สุดคือคนที่โดนเก็บข้อมูลโดยไม่มีโอกาสปฏิเสธเลย ที่เสียไปคือข้อมูลของผู้ใช้กลุ่มที่ปิด JavaScript ซึ่งน้อยมากอยู่แล้ว เทียบกับการเปิดช่องโหว่ที่ตรวจแล้วอธิบายไม่ได้ ผมเลือกไม่ใส่

AdSense: ซ่อนช่องไว้ก่อน แล้วค่อย push ตอนยอมรับ

เว็บที่รันโฆษณามีเงื่อนไขเพิ่มอีกชั้น คือช่อง <ins class="adsbygoogle"> ถูกวางไว้ในหน้าตั้งแต่แรก ถ้าไม่ทำอะไรเลย มันจะเป็นกล่องว่างค้างอยู่กลางเนื้อหาระหว่างที่ยังไม่ยอมรับ วิธีที่ผมใช้คือซ่อนด้วย CSS ที่ผูกกับ class บน <html> แล้วปลดตอนยอมรับ

/* ซ่อนช่องโฆษณาตราบใดที่ยังไม่ได้รับความยินยอม — ไม่ให้เหลือช่องว่างในเนื้อหา */
html.ck-noads .adsbygoogle,
html.ck-noads .ad-slot { display: none !important; }
// ส่วนของ AdSense ใน consent.js — เรียกจาก accept() เท่านั้น
var ADS_CLIENT = 'ca-pub-XXXXXXXXXXXXXXXX';   // เปลี่ยนเป็นของตัวเอง
var adsLoaded = false;

function showAds() {
  if (adsLoaded) return;
  adsLoaded = true;

  var s = document.createElement('script');
  s.async = true;
  s.crossOrigin = 'anonymous';
  s.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=' + ADS_CLIENT;
  s.onload = function () {
    // สั่งเรนเดอร์ช่องที่มีอยู่แล้วในหน้า หนึ่ง push ต่อหนึ่งช่อง
    var slots = document.querySelectorAll('.adsbygoogle:not([data-adsbygoogle-status])');
    for (var i = 0; i < slots.length; i++) {
      try { (window.adsbygoogle = window.adsbygoogle || []).push({}); } catch (e) {}
    }
  };
  document.head.appendChild(s);
}
push ต้องนับตามช่องที่ยังไม่ถูกเรนเดอร์

ตัวเลือก .adsbygoogle:not([data-adsbygoogle-status]) สำคัญกว่าที่คิด เพราะช่องที่สคริปต์เรนเดอร์ไปแล้วจะมีแอตทริบิวต์นั้นติดอยู่ ถ้าไปนับรวมแล้ว push เกินจำนวนช่องจริง จะได้ error ว่า All ins elements already have ads in them ในคอนโซล ซึ่งไม่ทำให้หน้าพัง แต่มันกลบ error ตัวอื่นที่สำคัญกว่าจนอ่านคอนโซลไม่รู้เรื่อง

ตรวจด้วยเบราว์เซอร์จริง: request ปลายทางต้องเป็นศูนย์

ทั้งหมดที่เขียนมาจะเชื่อถือได้ก็ต่อเมื่อมีตัวเลขยืนยัน ผมเขียนตัวตรวจที่เปิดหน้าเว็บ ดักทุก request แล้วนับเฉพาะที่วิ่งไปหาโดเมนปลายทางของ GTM, GA4 และ AdSense ต้องได้ศูนย์ก่อนกด และต้องมากกว่าศูนย์หลังกดยอมรับ

// tools/consent-check.cjs — ด่านที่ตอบว่าแบนเนอร์กั้นได้จริงหรือเปล่า
const { chromium } = require('playwright');

const WATCH = ['googletagmanager.com', 'google-analytics.com', 'analytics.google.com',
               'googlesyndication.com', 'doubleclick.net'];
const hits = [];

(async () => {
  const b = await chromium.launch();
  const p = await b.newPage();
  p.on('request', r => { if (WATCH.some(d => r.url().includes(d))) hits.push(r.url()); });

  await p.goto('https://example.com/?v=' + Date.now(), { waitUntil: 'networkidle' });
  await p.waitForTimeout(2500);

  const before = hits.length;
  console.log(`[${before === 0 ? 'OK' : 'FAIL'}] ก่อนกดยอมรับ: ${before} request`);
  hits.forEach(u => console.log('       ', u));

  const bar = await p.$('#ck-bar:not([hidden])');
  console.log(`[${bar ? 'OK' : 'FAIL'}] แบนเนอร์แสดงอยู่`);

  await p.click('#ck-yes');
  await p.waitForTimeout(3000);
  const after = hits.length - before;
  console.log(`[${after > 0 ? 'OK' : 'FAIL'}] หลังกดยอมรับ: ${after} request`);

  // กดยอมรับแล้วโหลดใหม่ ต้องไม่ถามซ้ำ และต้องไม่โหลด container ซ้ำซ้อน
  const mark = hits.length;
  await p.reload({ waitUntil: 'networkidle' });
  await p.waitForTimeout(2000);
  const gtm = hits.slice(mark).filter(u => u.includes('gtm.js')).length;
  console.log(`[${gtm === 1 ? 'OK' : 'FAIL'}] โหลดซ้ำหน้าเดิม: gtm.js = ${gtm} ครั้ง`);

  await b.close();
  process.exit(before === 0 && after > 0 && gtm === 1 ? 0 : 1);
})();
$ node tools/consent-check.cjs
[OK] ก่อนกดยอมรับ: 0 request
[OK] แบนเนอร์แสดงอยู่
[OK] หลังกดยอมรับ: 7 request
[OK] โหลดซ้ำหน้าเดิม: gtm.js = 1 ครั้ง

บรรทัดสุดท้ายคือตัวที่ผมเพิ่มทีหลังหลังจากเผลอเรียก loadGTM() สองที่ในโค้ดเวอร์ชันก่อน แล้วยอดผู้เข้าชมสูงเป็นสองเท่าอยู่สามวัน ตัวกันเรียกซ้ำในฟังก์ชันช่วยได้ก็จริง แต่ตัวตรวจที่นับให้เห็นเป็นตัวเลขดีกว่าการเชื่อว่าเราเขียนกันไว้แล้ว

ถอนความยินยอมได้ และสรุปเป็นขั้นตอน

ข้อสุดท้ายที่คนลืมกันบ่อยคือคนที่กดไปแล้วต้องเปลี่ยนใจได้ ถ้าไม่มีทางกลับ ก็เท่ากับถามครั้งเดียวตลอดชีวิต ผมวางปุ่มเล็ก ๆ ไว้ที่ท้ายเว็บทุกหน้า

<!-- ท้ายเว็บ ข้าง ๆ ลิงก์นโยบาย -->
<button type="button" class="foot-link" onclick="ckManage()">จัดการคุกกี้</button>
0request ก่อนกดยอมรับ
1 ครั้งโหลด gtm.js ต่อหนึ่งหน้า
3สถานะที่ออกแบบไว้
128 pxความกว้างขั้นต่ำของปุ่ม
0 ตัวnoscript iframe ที่ใส่
5 โดเมนปลายทางที่ตัวตรวจเฝ้า
4 ด่านที่ตัวตรวจต้องผ่าน
3 วันที่ยอดเคยเกินสองเท่า
  1. เอาสคริปต์ GTM และ GA4 ออกจาก <head> ให้หมด ย้ายเข้าไปอยู่ในฟังก์ชัน loadGTM()
  2. ตั้ง GA4 เป็น tag ข้างใน container ตัวเดียว อย่าวาง gtag/js คู่ไว้ในหน้าอีก
  3. ออกแบบให้ครบสามสถานะ และอย่าถามซ้ำกับคนที่กดปฏิเสธไปแล้ว
  4. ทำปุ่มยอมรับกับปฏิเสธให้มีน้ำหนักเท่ากัน ทั้งขนาดและความชัด
  5. ซ่อนช่องโฆษณาด้วย class บน <html> ระหว่างที่ยังไม่ยอมรับ แล้ว push ตามจำนวนช่องที่ยังไม่ถูกเรนเดอร์
  6. ครอบทุกการอ่านเขียน localStorage ด้วย try และให้เส้นทางที่จำค่าไม่ได้ยังใช้งานเว็บได้ปกติ
  7. รันตัวตรวจที่นับ request ปลายทาง ต้องได้ศูนย์ก่อนกด และ gtm.js ต้องเท่ากับหนึ่งต่อหนึ่งหน้า
  8. วางปุ่มจัดการคุกกี้ไว้ท้ายเว็บทุกหน้า ให้คนเปลี่ยนใจได้เอง

เรื่องนี้ผมมองว่าไม่ใช่งานตามกฎอย่างเดียวนะครับ มันเป็นเรื่องที่ตรวจสอบได้ด้วยเครื่องมือที่ใครก็มี ใครจะเปิด DevTools ดูเว็บเราตอนไหนก็ได้ และสิ่งที่เขาเห็นจะตรงกับที่เราเขียนไว้ในหน้านโยบายหรือไม่ตรง มันมีคำตอบเดียว ส่วนจะเลือกทาง consent mode หรือกั้นทั้ง container ก็ดูจากว่าเว็บตัวเองพึ่งข้อมูลช่วงก่อนยินยอมแค่ไหน ตามสะดวกครับ ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

อยากให้เราทำให้ไหม?

ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า

คุยเรื่องงานกับเรา

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย