Introduksjon
CookiePilot er en plattform for samtykkehaandtering (CMP) som er i samsvar med GDPR (personvernforordningen), den norske ekomloven (lov om elektronisk kommunikasjon) og Google Consent Mode v2. GDPR gjelder i Norge gjennom EOES-avtalen, og etterlevelsen foeres av Datatilsynet.
Hvem det er for
- Nettstedseiere: ta det i bruk uten aa skrive kode.
- Utviklere: API, hendelser, GTM-integrasjoner.
- Byraaer: white-label, forvaltning av flere domener.
Hurtigstart
Steg 1: Registrer deg
- Opprett en konto paa app.cookiepilot.io/register.
- Legg til domenet ditt i panelet og kopier API-noekkelen (format
cp_live_...).
Steg 2: Installer koden
En direkte installasjon er to skript i <head>, i denne rekkefoelgen.
Steg 2a: Standardsamtykke (inline stub). Lim inn dette foerst, foer alle andre skript (cookiepilot.js, GA, GTM, annonse- og sporingstagger):
<script>"use strict";(function(){window.dataLayer=window.dataLayer||[];var d={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},h=false;try{for(var i=0;i<window.dataLayer.length;i++){var x=window.dataLayer[i];if(x&&x[0]==="consent"&&x[1]==="default"){h=true;break}}}catch(err){}if(!h&&window.dataLayer.length)window.dataLayer.unshift(["consent","default",d]);window.gtag=function(){window.dataLayer.push(arguments)};if(!h)window.gtag("consent","default",d);var a=document.cookie.match(/(^|)cookiepilot_consent=([^;]+)/);if(a){try{var e=JSON.parse(decodeURIComponent(a[2]));window.gtag("consent","update",{analytics_storage:e.analytics?"granted":"denied",ad_storage:e.marketing?"granted":"denied",ad_user_data:e.marketing?"granted":"denied",ad_personalization:e.marketing?"granted":"denied",functionality_storage:e.preferences?"granted":"denied",personalization_storage:e.preferences?"granted":"denied",security_storage:"granted"})}catch(err){}}})();</script>
Denne inline-snutten setter umiddelbart hver kategori til denied (med wait_for_update: 500), slik at standardtilstanden for Google Consent Mode v2 er klar foer noe annet lastes. For en tilbakevendende besoekende leser den det lagrede samtykket rett fra cookien og sender en consent update.
Steg 2b: Bannerskript. Legg dette til rett etter inline-stubben:
<!-- CookiePilot -->
<script async src="https://cdn.cookiepilot.io/cookiepilot.js" data-cpkey="TWOJ_KLUCZ"></script>
Steg 3: Konfigurer banneren
I panelet: Domene → Konfigurasjon → Utseende:
- bannerplassering (topp, bunn, modal),
- farger og tekst,
- en flytende «Cookie-innstillinger»-knapp for tilbakevendende besoekende.
Integrasjon med Google Tag Manager
Gjennom GTM installerer du CookiePilot med en enkelt Custom HTML-tag som setter standardsamtykket og laster banneren i den tidligste GTM-fasen. Du trenger ingen egen stub-fil eller en ekstra tag.
Tag: CookiePilot - Consent Init + Banner
I GTM oppretter du en Custom HTML-tag (Tags → New → Custom HTML) og limer inn:
<script>"use strict";
(function() {
window.dataLayer=window.dataLayer||[];var d={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},h=false;try{for(var i=0;i<window.dataLayer.length;i++){var x=window.dataLayer[i];if(x&&x[0]==="consent"&&x[1]==="default"){h=true;break}}}catch(err){}if(!h&&window.dataLayer.length)window.dataLayer.unshift(["consent","default",d]);window.gtag=function(){window.dataLayer.push(arguments)};if(!h)window.gtag("consent","default",d);var a=document.cookie.match(/(^|)cookiepilot_consent=([^;]+)/);if(a){try{var e=JSON.parse(decodeURIComponent(a[2]));window.gtag("consent","update",{analytics_storage:e.analytics?"granted":"denied",ad_storage:e.marketing?"granted":"denied",ad_user_data:e.marketing?"granted":"denied",ad_personalization:e.marketing?"granted":"denied",functionality_storage:e.preferences?"granted":"denied",personalization_storage:e.preferences?"granted":"denied",security_storage:"granted"})}catch(err){}}
var s = document.createElement('script');
s.src = 'https://cdn.cookiepilot.io/cookiepilot.js?cpkey=' + encodeURIComponent('TWOJ_KLUCZ');
document.head.appendChild(s);
})();
</script>
Trigger: Consent Initialization - All Pages. Taggen maa utloeses en gang per side. Google-tagger (GA4, Google Ads), Facebook Pixel og andre markedstagger bruker IKKE Consent Initialization-triggeren; de utloeses senere (Consent Checks eller en samtykkeavhengig trigger). Lagre taggen og publiser GTM-containeren.
Rekkefoelge og triggere
| Rekkefoelge | Tag | Trigger |
|---|---|---|
| 1 | CookiePilot - Consent Init + Banner | Consent Initialization - All Pages |
| 2 | GA4, Google Ads, UET | All Pages (Consent Mode haandterer samtykket selv) |
| 3 | Facebook Pixel, TikTok, LinkedIn osv. | Custom Event cookiepilot_consent_update + samtykkebetingelse (se nedenfor) |
Tagger utenom Google (Facebook Pixel, TikTok, LinkedIn)
Google Consent Mode dekker bare Google-tagger. For alle andre skript pusher widgeten en hendelse til dataLayer ved hver samtykkeendring:
dataLayer.push({
event: 'cookiepilot_consent_update',
cookiepilot_consent: {
necessary: true,
analytics: true,
marketing: true,
preferences: false
}
});
Hendelsen utloeses ogsaa ved hvert besoek fra en tilbakevendende bruker (naar widgeten leser samtykkecookien), saa GTM-triggeren virker ved hvert besoek, ikke bare ved den foerste beslutningen.
Felles moenster (gjoer dette en gang)
Du konfigurerer disse tre delene en gang, og gjenbruker dem for alle tagger utenom Google.
- Trigger (Triggers → New → Custom Event): Event name
cookiepilot_consent_update. Ingen betingelser, ingen «Once per page» (taggen maa kunne utloeses paa nytt etter en endret beslutning). - Data Layer Variable for hver kategori du bruker:
- Name
cookiepilot_consent.marketing→ variabel f.eks.dlv.cp_marketing - Name
cookiepilot_consent.analytics→ variabel f.eks.dlv.cp_analytics - Name
cookiepilot_consent.preferences→ variabel f.eks.dlv.cp_preferences
- Name
- Trigger Group eller en betingelse paa triggeren:
dlv.cp_marketing equals true(for markedstagger) eller det relevante feltet.
I hvert av eksemplene nedenfor er Trigger den samme Custom Event med en betingelse lagt til paa riktig variabel.
Facebook Pixel
Tag i GTM: Tags → New → Custom HTML.
<script>
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', 'YOUR_PIXEL_ID');
fbq('track', 'PageView');
</script>
- Trigger:
cookiepilot_consent_update+ betingelsedlv.cp_marketing equals true. - Tag firing options: Once per page.
Valgfritt, for full etterlevelse av Facebook Limited Data Use, legg til en ekstra tag som kaller fbq('consent','revoke') med triggeren dlv.cp_marketing equals false.
TikTok Pixel
Tag i GTM: Tags → New → Custom HTML.
<script>
!function (w, d, t) {
w.TiktokAnalyticsObject=t;var ttq=w[t]=w[t]||[];ttq.methods=["page","track","identify","instances","debug","on","off","once","ready","alias","group","enableCookie","disableCookie","holdConsent","revokeConsent","grantConsent"],ttq.setAndDefer=function(t,e){t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}};for(var i=0;i<ttq.methods.length;i++)ttq.setAndDefer(ttq,ttq.methods[i]);ttq.instance=function(t){for(var e=ttq._i[t]||[],n=0;n<ttq.methods.length;n++)ttq.setAndDefer(e,ttq.methods[n]);return e},ttq.load=function(e,n){var r="https://analytics.tiktok.com/i18n/pixel/events.js",o=n&&n.partner;ttq._i=ttq._i||{},ttq._i[e]=[],ttq._i[e]._u=r,ttq._t=ttq._t||{},ttq._t[e]=+new Date,ttq._o=ttq._o||{},ttq._o[e]=n||{};n=document.createElement("script");n.type="text/javascript",n.async=!0,n.src=r+"?sdkid="+e+"&lib="+t;e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(n,e)};
ttq.load('YOUR_TIKTOK_PIXEL_ID');
ttq.grantConsent();
ttq.page();
}(window, document, 'ttq');
</script>
- Trigger:
cookiepilot_consent_update+ betingelsedlv.cp_marketing equals true. - Tag firing options: Once per page.
ttq.grantConsent() er TikToks nye API (innfoert i 2024). Uten det mottar TikTok hashede data uten samtykke, noe som bryter vilkaarene.
LinkedIn Insight Tag
<script type="text/javascript">
_linkedin_partner_id = "YOUR_LINKEDIN_PARTNER_ID";
window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];
window._linkedin_data_partner_ids.push(_linkedin_partner_id);
</script>
<script type="text/javascript">
(function(l) {
if (!l){window.lintrk = function(a,b){window.lintrk.q.push([a,b])};
window.lintrk.q=[]}
var s = document.getElementsByTagName("script")[0];
var b = document.createElement("script");
b.type = "text/javascript";b.async = true;
b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js";
s.parentNode.insertBefore(b, s);})(window.lintrk);
</script>
- Trigger:
cookiepilot_consent_update+ betingelsedlv.cp_marketing equals true. - Tag firing options: Once per page.
Hotjar
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:YOUR_HOTJAR_ID,hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-',".js?sv=");
</script>
- Trigger:
cookiepilot_consent_update+ betingelsedlv.cp_analytics equals true. - Tag firing options: Once per page.
Hotjar hoerer under analytics, ikke marketing (det maaler atferd, ikke annonser). Sjekk din egen cookie-policy, noen selskaper klassifiserer Hotjar annerledes.
Microsoft Clarity
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "YOUR_CLARITY_PROJECT_ID");
</script>
- Trigger:
cookiepilot_consent_update+ betingelsedlv.cp_analytics equals true. - Tag firing options: Once per page.
Pinterest Tag
<script>
!function(e){if(!window.pintrk){window.pintrk = function () {
window.pintrk.queue.push(Array.prototype.slice.call(arguments))};var
n=window.pintrk;n.queue=[],n.version="3.0";var
t=document.createElement("script");t.async=!0,t.src=e;var
r=document.getElementsByTagName("script")[0];
r.parentNode.insertBefore(t,r)}}("https://s.pinimg.com/ct/core.js");
pintrk('load', 'YOUR_PINTEREST_TAG_ID');
pintrk('page');
</script>
- Trigger:
cookiepilot_consent_update+ betingelsedlv.cp_marketing equals true. - Tag firing options: Once per page.
Microsoft Ads (UET)
UET har stoettet Google Consent Mode siden slutten av 2023, saa du trenger ikke en Custom Event-trigger. Legg til taggen paa standard maate (All Pages, Once per page), og UET leser ad_storage fra GCM selv, som widgeten setter.
Kategorikartlegging (oppsummering)
| Tag | Kategori | Felt i cookiepilot_consent |
|---|---|---|
| Facebook Pixel | marketing | marketing |
| TikTok Pixel | marketing | marketing |
| LinkedIn Insight | marketing | marketing |
| marketing | marketing | |
| Hotjar | analytics | analytics |
| Microsoft Clarity | analytics | analytics |
| Mixpanel, Amplitude | analytics | analytics |
| Intercom, Drift, Crisp | preferences | preferences |
| GA4, Google Ads, UET | (GCM, ingen trigger) | haandtert av gtag('consent','update') |
API-referanse
CookiePilot eksponerer objektet window.CookiePilot:
| Metode | Beskrivelse |
|---|---|
CookiePilot.getConsent() | Naavaerende samtykketilstand, eller null hvis ingen beslutning er tatt. |
CookiePilot.acceptAll() | Samtykk til alle kategorier. |
CookiePilot.rejectAll() | Avviser alt bortsett fra necessary. |
CookiePilot.updateConsent(partial) | Oppdaterer valgte kategorier, f.eks. { analytics: true }. |
CookiePilot.showSettings() | Aapner innstillingsmodalen. |
CookiePilot.hideSettings() | Lukker innstillingsmodalen. |
CookiePilot.showMyConsent() / hideMyConsent() | Viser/skjuler den flytende knappen. |
Eksempel
const consent = CookiePilot.getConsent();
// { necessary: true, analytics: true, marketing: false, preferences: false }
CookiePilot.updateConsent({ analytics: true });
document.getElementById('cookie-settings').addEventListener('click', () => {
CookiePilot.showSettings();
});
«Administrer cookies»-lenke i bunnteksten
<a href="#" onclick="CookiePilot.showSettings(); return false;">Administrer cookies</a>
JavaScript-hendelser
Widgeten sender en nativ cookiepilot:consent-hendelse paa window. Lytteren maa registreres foer widgeten lastes hvis du vil fange hendelsen for en tilbakevendende besoekende (utsendelsen skjer umiddelbart naar widgeten starter):
<script>
window.addEventListener('cookiepilot:consent', (e) => {
if (e.detail.marketing) {
fbq('init', 'YOUR_PIXEL_ID');
}
});
</script>
<script src="https://cdn.cookiepilot.io/cookiepilot.js" data-cpkey="TWOJ_KLUCZ"></script>
e.detail inneholder samme payload som getConsent(). For GTM-baserte integrasjoner bruker du dataLayer-hendelsen beskrevet ovenfor i stedet for denne, fordi dataLayer er et vedvarende array og GTM plukker opp historiske hendelser.
Samtykkekategorier
| Kategori | Beskrivelse | Standard |
|---|---|---|
necessary | Noedvendig for at nettstedet skal virke | Alltid paa |
analytics | Statistikk og analyse | Krever samtykke |
marketing | Annonsering og remarketing | Krever samtykke |
preferences | Tilpasning, spraak | Krever samtykke |
Kartlegging til Google Consent Mode
| CookiePilot-kategori | Consent Mode-felt |
|---|---|
analytics | analytics_storage |
marketing | ad_storage, ad_user_data, ad_personalization |
preferences | functionality_storage, personalization_storage |
| (alltid) | security_storage: granted |
Google Consent Mode v2
Versjon 2 la til ad_user_data og ad_personalization (paakrevd siden mars 2024 i EOES for Google-annonsering).
| Parameter | Beskrivelse |
|---|---|
ad_storage | Annonsecookies |
analytics_storage | Analysecookies |
ad_user_data | Sender brukerdata til Google |
ad_personalization | Annonsetilpasning |
functionality_storage | Funksjonelle cookies |
personalization_storage | Tilpasningscookies |
security_storage | Alltid granted |
Slik virker det:
- Stubben setter hvert felt til
deniedsynkront, medwait_for_update: 500. - Etter brukerens beslutning sender widgeten
gtag('consent', 'update', {...})ved hjelp av kartleggingen ovenfor. - For en tilbakevendende besoekende skjer steg 2 umiddelbart naar widgeten starter, basert paa cookien.
Utseendekonfigurasjon
I panelet: Domene → Konfigurasjon:
- Utseende: plassering, farger, layout (BAR / BOX / MODAL).
- Tekst: overskrift, beskrivelse, knappetekster, kategoribeskrivelser. 13 spraak (EN, PL, DE, FR, ES, IT, NL, PT, SV, CS, RO, EL, HU).
- Samtykkeknapp: en flytende «Cookie-innstillinger»-knapp som vises etter den foerste beslutningen (nederst til venstre/hoeyre).
- Custom CSS: et felt for dine egne stiler. Widgeten rendres i Shadow DOM, saa CSS-selektorer fra hoveddokumentet virker ikke. Bruk kun dette feltet.
Tilgjengelighet (WCAG 2.1 AA)
- ✅ Tastaturnavigasjon (Tab, Shift+Tab, Enter, Escape).
- ✅ ARIA-labels, role="dialog", aria-modal.
- ✅ Fokusfelle i modalen.
- ✅ Stoette for skjermlesere (live regions ved tilstandsendring).
- ✅ Responsivt design.
Integrasjoner
WordPress
Vi har en offisiell plugin: CookiePilot paa WordPress.org.
- WordPress-admin → Plugins → Add New → soek etter «CookiePilot».
- Installer og aktiver.
- Settings → CookiePilot → lim inn API-noekkelen fra panelet.
Pluginen setter selv inn stubben og taggen i <head> (i riktig rekkefoelge, foran andre skript) og tilbyr en [cookiepilot_settings]-shortcode for en «Administrer cookies»-lenke i bunnteksten.
Foretrekker du å ikke bruke pluginen, bruk «Insert Headers and Footers» og lim inn snutten fra Steg 2 i Hurtigstart i Header-seksjonen.
Shopify
- Butikk → Themes → Edit code.
- I
theme.liquid, lim inn snutten foer</head>.
Next.js
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html>
<head>
<Script
src="https://cdn.cookiepilot.io/cookiepilot.js"
data-cpkey="TWOJ_KLUCZ"
strategy="beforeInteractive"
/>
</head>
<body>{children}</body>
</html>
);
}
strategy="beforeInteractive" sikrer at bannerskriptet kjoerer tidlig. Husk at inline-stubben fra Steg 2a (standardsamtykke) legges til separat, i <head> foran dette skriptet, for eksempel via next/script med dangerouslySetInnerHTML eller direkte i app/layout.tsx.
FAQ
Bremser skriptet nettstedet?
Bundelen er rundt 12 KB gzippet og lastes asynkront. Ingen paavirkning paa Core Web Vitals.
Hvor lenge lagres samtykket?
Cookien med brukerens beslutning: 365 dager som standard (konfigurerbart i panelet). Analysehendelser: 2 aar (ClickHouse TTL).
Utloeses cookiepilot_consent_update-hendelsen for tilbakevendende besoekende?
Ja. Widgeten leser cookien ved oppstart og pusher hendelsen til dataLayer. Custom Event-triggeren i GTM utloeses ved hvert besoek, ikke bare ved den foerste beslutningen.
Hvor melder jeg fra om et problem?
kontakt@cookiepilot.io eller chatten i panelet.
Testsjekkliste etter utrulling
Naar CookiePilot er installert, verifiser konfigurasjonen foer du starter annonsekampanjer.
1. Test foer brukeren gir samtykke
- Aapne nettstedet i inkognitomodus og toem cookies for domenet.
- Aapne DevTools → Network og last siden paa nytt.
- Foer du klikker paa samtykke, bekreft at:
- ingen analyse-/markedscookies opprettes, f.eks.
_ga,_gcl_*,_fbp,_ttp, - markedstagger ikke sender foresporsler til Meta/TikTok/LinkedIn foer markedssamtykke,
dataLayerhar standardtilstandendeniedforad_storage,ad_user_data,ad_personalizationoganalytics_storage.
- ingen analyse-/markedscookies opprettes, f.eks.
2. Test etter aksept av samtykke
- Klikk «Godta alle».
- I DevTools, sjekk at hendelsen
cookiepilot_consent_updatedukket opp. - For Google-tagger, sjekk i GTM Preview / Tag Assistant at Consent Mode byttet tilstanden til
grantedfor de relevante kategoriene. - For GA4, sjekk DebugView for aa bekrefte at hendelser begynner aa komme inn etter samtykke.
- For Meta Pixel / TikTok / LinkedIn, sjekk at taggene bare utloeses etter
cookiepilot_consent_update-hendelsen og betingelsen for markedssamtykke.
3. Test avvisning av samtykke
- Toem cookies og last siden paa nytt.
- Klikk «Avvis alle».
- Bekreft at markedscookies og foresporsler fremdeles ikke utloeses.
- Bekreft at noedvendige funksjoner paa nettstedet fremdeles virker.
4. Vanlige feil
- GA4-/Google Ads-taggen utloeses paa
Consent Initializationi stedet for en senere trigger. - Meta Pixel eller TikTok Pixel har ingen betingelse paa
cookiepilot_consent.marketing. - En gammel, hardkodet sporer ligger fortsatt i
<head>foer CookiePilot. - Dokumentasjonen eller malen har fortsatt en utdatert skript-URL eller et gammelt identifikatorattributt i stedet for den gjeldende
cookiepilot.jsmeddata-cpkey.
Samtykkebevis, eksporter og DSAR
CookiePilot registrerer samtykkehendelser slik at du kan rekonstruere konteksten for en besoekendes beslutning. En registrering kan inneholde tidspunktet for hendelsen, hendelsestype, domene, side- eller kilde-URL, samtykkeversjon, kategorier og consent string naar den er tilgjengelig, anonymisert IP-adresse, hash av user-agent, enhetstype og nettleser.
Eksporter
- Samtykkebevis for en besoekende: JSON, CSV eller utskrivbar HTML.
- Revisjonslogg for domenet: CSV eller JSON for et valgt datointervall.
- Eksporter er avgrenset til organisasjonen din og til domenene du har tilgang til.
DSAR-arbeidsflyt
En Owner eller Admin kan starte en innsynsbegjaering fra en samtykkerad eller fra DSAR-siden i panelet. Panelet finner hendelsene til den besoekende, viser intervallet og lar deg laste ned bevisfiler. En sletteforesporsel krever en begrunnelse og logges i organisasjonens aktivitetsspor.
Begrensninger
Revisjonseksporter for domenet bruker det valgte datointervallet og kan bli avkortet for svaert store resultatsett. Oppbevaringen av samtykkeloggen foelger den aktive planen og kontopolitikken, saa den offentlige dokumentasjonen lover ikke et fast antall aar.