Markus Baersch

Analytics · Beratung · Lösungen

Start » Blog » Datenschutz / Google Tag Manager / Trackingschutz

19.03.2022

Das Laden von Ressourcen über Drittserver ist heute nicht nur dem Risiko unterworfen, vom Browser blockiert zu werden, sondern ist je nach Ressourcen-Quelle in der aktuellen Lage auch zum Datenschutzrisiko geworden. Vor allem, wenn die Quelle ein Google-Server ist. Google Webfonts sind ein bekanntes Beispiel, in dem jüngst im Rahmen eines Urteils behauptet wurde, es sei dem typischen Webmaster zuzumuten, ein lokales Hosting für solche Ressourcen einzurichten. Ich sehe das in der Praxis zwar etwas anders, dennoch bleibt die Frage: Wenn es ein Problem ist, Google Fonts vom Google Server zu laden, warum sollte es beim Tag Manager Container anders sein?

Wenngleich ein lokal bereitgestellter GTM Container nicht alle Probleme lösen kann, die ein damit verbautes Tracking für Google Analytics oder Ads nach sich zieht, gibt es dennoch realistische Szenarien, in denen die Maßnahme sinnvoll sein kann. Also: Container Script runterladen, auf den eigenen Server und fertig? Im Prinzip ja. Obwohl das Konsequenzen hat für das Debugging, den eigenen Server und... aber fangen wir vorn an.

Was bringt ein "selbst gehosteter" GTM Container?

Vor dem Wie lohnt es sich, einen zweiten Blick auf das Warum zu werfen. Denn nicht ohne Grund kann man z. B. mit einem serverseitigen Google Tag Manager Container nicht nur Trackinghits entgegennehmen und verarbeiten, sondern auch den clientseitigen GTM Container darüber laden (ebenso wie die für Analytics erforderlichen Scripts).

Robuster gegen Trackingschutz

Im Vordergrund steht hier nicht unbedingt der Datenschutz, sondern eine größere Chance, dass der client-seitige GTM Container und die Tracking-Scripts geladen werden. Denn moderne Browser blockieren je nach Datenschutzeinstellungen (vor allem Firefox und Edge) oder von Haus aus (z. B. Brave) vieles, was entweder generell von Drittparteien oder speziell von bekannten Tracking-Servern stammt. Während ein statischer, lokal gehosteter GTM Container in Form einer JavaScript-Datei auf dem eigenen Server kein vollwertiger Ersatz für einen server-side GTM ist, kann man so zumindest für den Container selbst so das gleiche Ziel erreichen: Deutlich erhöhte Wahrscheinlichkeit, dass der Container wirklich geladen wird.

Raus aus der Consent-Falle

In vielen Consent Managern findet man einen Eintrag für den Google Tag Manager. Gern steht dort sogar etwas von Cookies, die der GTM setzt - so sehr sind Google Tag Manager und Google Analytics in vielen Köpfen untrennbar miteinander verbunden. Diese Einschätzung ist grundsätzlich falsch. Die einzigen Cookies, die im Spiel sind, passier(t)en im Kontext der Vorschau auf dem Rechner des GTM-Administrators und nicht generell bei einem normalen Besuch der Website. Dennoch ist es eine Ressource von einem Google Server und als solche derzeit eben unter Feuer (das kommt eher von der DSGVO- und nicht der TTGSG/ePrivacy-Front).

Ob man nun den GTM im Consent Tool aufführen sollte oder nicht (ich denke: Nein): Eine andere Klassifizierung als "notwendig" kreiert ein Problem, denn sobald man im Consent den GTM individuell abwählen, aber andere Dinge aktivieren kann (die über den GTM ausgespielt werden sollen), steckt man in der Logikfalle. Auf der anderen Seite will man aber ggf. weder den GTM Container, noch jQuery, Webfonts oder sonstwas von Google Servern laden, ohne vorher gefragt zu haben. Und wie bei jQuery oder Fonts kann auch der GTM Container durch eine Bereitstellung auf dem eigenen Server aus diesem Dilemma befreit werden. Ein client-seitiger GTM Container ist nichts anderes als eine JavaScript-Datei, welche die im Container vorhandenen Tags, Trigger und Variablen und den zur Ausführung nach den im GTM definierten Regeln erforderlichen Code enthält. Eine ganz normale JavaScript Ressource also.

Kommt diese vom eigenen Server, kann man den Google Tag Manager Code bedenkenlos einbinden. Sei es erst nach Zustimmung zu "essenziellen Cookies" (seufz) oder unabhängig von Zustimmung und ohne dort aufgeführt zu werden. Jedenfalls so lange, wie im GTM dafür gesorgt ist, dass Tags nur dann geladen und ausgeführt werden, wenn der Consent dies zulässt. Das sollte in jedem ordentlich konfigurierten und auf das Consent Tool abgestimmtem Setup der Fall sein. Nebenbemerkung: Wer an den Weihnachtsmann Consent Mode glaubt, muss sich um solche Feinheiten keine Gedanken machen, denn er sendet ohnehin massenhaft Daten an Google, die das Problem einer IP, die beim Laden von Ressourcen weitergegeben wird, als einen Witz erscheinen lässt. Und ja: wir müssen reden 😉

Wirkungsvoll, solange kein Tracking stattfindet

Ist ein GTM Container aus Risikoperspektive zu einer stinknormalen lokalen JavaScript-Datei zurechtgestutzt, kann man damit eine ganze Menge tun. Komplette Tracking-Setups, die mit und ohne Zustimmung (und mit oder ohne Google Analytics) konzipiert je nach Consent-Lage ausgespielt werden, sind in diesem Fall "nur noch" den Beschränkungen unterworfen, die für die jeweiligen Dienste und deren Scripts etc. selbst gelten. Der GTM Web-Container ist nur noch das, was er sein sollte: Ein reines Werkzeug für Tagging und ggf. funktionale oder inhaltliche Erweiterung der eigenen Website ohne Datenschutz-Implikationen. Echtes "First Party Measurement" oder der Einsatz von alternativen Tracking-Tools bei einem Umstieg von GA zu irgendwas kann auf diese Weise weiterhin auf dem GTM aufbauen und die vielfältigen Möglichkeiten von dataLayer, Variablen und komplexen Trigger-Mechanismen nutzen, die evtl. bereits aufgebaut wurden.

Einschränkungen und Lösungsansätze

Nach all den Vorteilen mag man sich fragen: Wenn es so viel besser ist, einen GTM Container lieber auf den eigenen Server zu legen, warum macht es denn nicht jeder? Das liegt hauptsächlich an diesen Punkten:

  1. Aktualisierung: Sobald eine neue Fassung des Containers veröffentlicht wird, muss diese einen Weg auf den eigenen Server finden. Das würde manuellen Aufwand bedeuten, wenn man das Script wirklich im Browser abrufen, speichern und via FTP auf dem eigenen Server ablegen würde. Genau deshalb lädt man eben lieber immer das aktuelle Script von googletagmanager.com, statt mit einer lokalen Kopie zu arbeiten. Auch sorgt die Bereitstellung durch Google dafür, dass das Script nicht zu lange im Cache verbleibt und so neue Versionen zeitnah im Browser landen
  2.  Verlust der Vorschau: Will man seine Änderungen im GTM in der Vorschau kontrollieren, muss der Container zwingend vom Google Server stammen (OK, das stimmt nicht ganz, aber nehmen wir es der Einfachheit halber an). Auch dazu gibt es Lösungen (die gleich folgen), dennoch bleibt unbestreitbar ein Mehraufwand, den man durch den Einsatz des GTM auf die "normale" Weise nicht hat
  3. Nur eine Teillösung: Solange ein GTM lokal gehostet wird, aber nach wie vor mit Google Analytics gearbeitet werden soll, bleibt das Problem bestehen, dass Scripts wie gtag.js, analytics.js und Ergänzungen wie ec.js bei Verwendung normaler Tags im GTM nach wie vor vom Google Server kommen. Es gibt Auswege (allen voran einen server-side GTM), die darüber hinaus das Problem des Endpunktes lösen, so dass Scripts vom eigenen Server und Hits an den eigenen Server gesendet werden. Hier soll / muss es reichen, das Third-Party-Problem isoliert für den GTM Container zu lösen.

Die gute Nachricht: Die ersten beiden Einschränkungen sind deutlich abzumildern - und der dritte Punkt ist nur dann relevant, wenn man mit Google Tags arbeiten will. Was wie oben beschrieben nicht immer der Fall sein muss.

Aktualisierungsprozess vereinfachen

Manueller Down- und Upload des eigenen Containers sind nur dann eine Lösung, wenn man ein eher statisches Setup hat und selten (bis nie) eine neue Version des GTM veröffentlichen muss. Das wird in den wenigsten Fällen gelten. Daher ist es eine gute Idee, den Prozess so weit wie möglich zu vereinfachen oder komplett zu automatisieren.

So ist es denkbar, den GTM Container z. B. von einer PHP Datei als "Proxy" auf dem eigenen Server abzurufen, welche den Container von googletagmanager.com abruft und weiterreicht. Damit das nicht jedes Mal wieder passieren muss, wenn ein GTM von einem Browser geladen werden soll, kann man den Inhalt des Containers auf dem Server in einem Cache halten und nur alle x Minuten verwerfen und neu abrufen. Der server-side GTM macht im Prinzip genau das. Damit bürdet man dem eigenen Server deutlich mehr Arbeit auf als das Bereitstellen einer statisch auf dem Server abgelegten JS-Datei. Daher ist es ein guter Kompromiss, einen "Aktualisierungsservice" auf dem eigenen Server einzurichten, den man bei Bedarf aufrufen kann, wenn eine neue Version des GTM veröffentlicht wurde und daher die lokale Kopie aktualisiert werden muss. Das ist sogar komplett automatisierbar

Hinter einem .htaccess Schutz gesichert, ist ein solcher kleiner Helfer auf dem eigenen Server schnell aufgerufen, wann immer eine neue GTM Version erstellt wurde. Ein Beispiel, auf dem man seinen eigenen Service aufbauen kann, findet sich unten in diesem Beitrag.

Debugging (re-)aktivieren

Um dennoch in den Genuss eines Live Containers zu kommen, der in der Vorschau genutzt und auf gewohnte Weise zu kontrollieren ist, kann man mehrere Wege beschreiten. Für das gelegentliche Debugging und eine überschaubare Anzahl von Websites und Containern, mit denen gearbeitet werden muss, reicht z. B. vielleicht schon das Einfügen des GTM Containers über entsprechende Plugins. Es gibt mehrere Erweiterungen für die verschiedenen Browser, die dies als Haupt- oder Nebenaufgabe anbieten. Ich habe selbst eine Erweiterung für Chrome erstellt, die dazu verwendet werden kann.

Komfortabler ist eine Lösung, die man auf einfache Weise aktivieren und wieder ausschalten kann. Wie z. B. durch das Erzeugen oder Löschen eines Cookies, sessionStorage / localStorage Eintrags o. Ä. Die Idee basiert also darauf, dass in Abhängigkeit eines lokalen Signals (das kann je nach Umsetzung sogar eine statische IP Adresse sein) nicht "wie bei allen anderen" die lokale Fassung des Containers geladen wird, sondern stets der Live Container.

Dies kann z. B. dadurch erzielt werden, dass das System den internen Besucher erkennt und ihm einen GTM - Script-Block in den Seiten ausspielt, der den Container direkt von googletagmanager.com lädt. Im Fall des o. a. Proxies kann dieser anhand der IP oder was auch immer ebenso entscheiden, den Live-Container zurückzuliefern, statt auf den Cache zuzugreifen.

Um weder Logik zur Steuerung unterschiedlicher Script-Varianten im CMS noch einen vergleichsweise ressourcenhungrigen Proxy zu benötigen, kann das GTM-Container-Script selbst zur Lösung beitragen. Passt man das GTM-Container-Script so an, dass bei Vorhandensein eines Cookies oder anderen Signals der Live-Container nachgeladen wird und der lokale Inhalt nur für alle anderen Browser relevant ist, ist keine weitere Logik erforderlich und der Server hat keine Arbeit. Stattdessen entscheidet sich im Browser, welche Fassung genutzt wird.

Im folgenden Script werden beide Punkte behandelt: Der Container kann durch einen einfachen Aufruf einer URL auf dem eigenen Server aktualisiert werden. Dabei wird die JavaScript Datei so ergänzt, dass eine "Cookie-Weiche" für ein Nachladen des Live-Containers sorgt, wenn die Vorschau genutzt werden soll. Keine manuellen Eingriffe erforderlich.

Beispiel-Lösung in PHP

Ich habe einen entsprechenden kleinen Helfer auf GitHub abgelegt, der die Verwaltung und Nutzung eines lokalen GTMs vereinfacht. Das ist weder die einfachste, noch eleganteste oder einzige Lösung, aber es mag allen helfen, die den GTM Container künftig selbst hosten wollen, ohne eine der o. a. Alternativen zu nutzen. Als Startpunkt für eine eigene Lösung ist es ganz sicher genug und selbst unverändert kann man damit (mit einer zusätzlichen Schicht Sicherheit wie oben und im Kommentar im Script angesprochen) in der Praxis arbeiten. Durch die (unnötig erscheinende) Nutzung von cURL statt file_get_contents sollten zudem die meisten Server ohne weitere Anpassungen damit klarkommen.

So funktioniert das Script

Neben ein paar Zeilen zur Konfiguration und reichlich Kommentaren besteht die Datei im Wesentlichen aus dem Abruf des GTM Containers von googletagmanager.com und der lokalen Speicherung der Kopie mit einer kleinen Ergänzung für das Debugging sowie einem Kommentar mit dem Datum des letzten Abrufs. Jeder Aufruf dieser Datei auf dem eigenen Server legt eine aktuelle Kopie des GTM Containers unter einem frei definierbaren Pfad und Dateinamen ab.

GTM Container laden und ergänzen

Die Ergänzung, welche in Zeile 26 der Abbildung für den Anfang der neuen lokalen Script-Datei definiert wird, bildet die "Mini-Cookie-Weiche", welche anhand der Existenz eines Cookies (hier: dbglive mit dem Wert 1) entscheidet, ob der lokale Container genutzt oder das Original vom Google-Server nachgeladen und der lokale Inhalt nicht ausgeführt wird. In Zeile 33 wird dieser Block (zusammen mit einem Cache-Update Zeitstempel) an den Anfang der lokalen Datei geschrieben, danach folgt der abgerufene Container. Am Ende wird noch eine schließende Klammer hinzugefügt und fertig ist der um Debugging-Fähigkeiten erweiterte lokale GTM-Container - keine große Magie also.

Helper installieren nutzen

Zur Bereitstellung ist es sinnvoll, ein bestehendes oder neues Verzeichnis mit .htaccess-Schutz oder ähnlichen Maßnahmen zu verwenden und die PHP Datei unter einem beliebigen Namen dort abzulegen. Den exemplarischen "Ersatzschutz" aus Zeile 18 und die ganzen warnenden Kommentare können in diesem Fall problemlos entfernt werden. Vor dem Upload ist es nur noch erforderlich, unter $gtm_save_path den gewünschten lokalen Pfad und Dateinamen und als $gtm_id die Container-ID zu definieren. BTW: "gtm.js" muss die Datei nicht unbedingt heißen, um zu funktionieren.

Nach einmaligem Ausführen der PHP Datei auf der Kommandozeile oder über den Browser und der schmucklosen Rückmeldung "cache updated"  entsteht im Idealfall eine lokale Datei mit dem aktuellen Container und die Arbeit ist getan, solange keine neue GTM Version erstellt wird. Erst dann muss der Vorgang wiederholt werden.

Lokal gespeicherter GTM Container

Nach dem Cache-Datum und der Bedingung für das Nachladen der Live-Version bei lokalem Debugging folgt der eigentliche GTM Container, so wie er vom Google Server ausgeliefert wird (gefolgt von einer schließenden Klammer der vorangestellten Bedingung). Die Versionsnummer ist dort als Teil des Codes leicht zu sehen, so dass ein Versions-Abgleich im Zweifelsfall auch anhand dieses Merkmals möglich ist.

GTM Container nutzen

Zur Verwendung der lokalen Version ist nun der Code anzupassen, mit dem der Tag Manager eingebunden wird. Was auch bedeutet, dass man mehr braucht als ein Feld, in das man eine Container-ID eingetragen werden kann. Entweder muss also der volle Code genutzt werden können - oder zumindest eine weitere Einstellung existieren, die die Definition eines eigenen Pfads zum Abruf des Containers erlaubt. Das ist nicht unwahrscheinlich, weil so z. B. ein server-side GTM genutzt werden kann, um den GTM zu laden, also ist die Anforderung nicht ganz so exotisch, wie es scheinen mag.

Am Beispiel des normalen GTM Codes (dessen noscript-Part man sich inzwischen übrigens problemlos sparen kann, so dass er hier bei der Anpassung keine Rolle spielt) wird der dortige Standard-Pfad für den Container ersetzt:

<!-- Google Tag Manager -->
<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),dl=l!="dataLayer"?"&l="+l:"";j.async=true;j.src=
"https://www.meinedomain.de.de/js/gtm.js?v=1"+dl;f.parentNode.insertBefore(j,f);
})(window,document,"script","dataLayer","%%gtmId%%");</script>
<!-- End Google Tag Manager -->

Hierbei wird angenommen, dass der neue Pfad der JavaScript-Datei mit dem Container /js/gtm.js lautet. Das „+i“ für die Container-ID in der Originalfassung muss / kann weg am Ende des Pfades, der ersatzweise hier einen anderen Parameter (v=1) erhalten hat, welcher eine Versionierung zum Erzwingen des Neuladens ermöglicht, wenn erforderlich (als "Nothammer" gegen im Browser gecachte Versionen).

Wird diese angepasste Implementierung auf der Website verwendet, wird der GTM wie gewünscht direkt vom eigenen Server geladen wie jede andere JS-Datei oder jedes lokal gespeicherte Bild auch. Enthält der GTM keine aktiven bzw. ohne Consent-Bedingungen feuernden Tags, passiert bei einem Erstbesuch also nichts, was aus Datenschutz-Sicht zweifelhaft wäre.

Debugging aktivieren

Um im eigenen Browser nicht mit der Cache-Version zu arbeiten, bedient sich der Helper eines Cookies. Denn im Prinzip ist ein Cookie so gut wie jedes andere Merkmal auch; schlussendlich hat es ja nur im Browser Relevanz, in dem die Vorschau genutzt werden soll. Alternative Optionen sind zudem oben bereits beschrieben.

Das im Beispiel genutzte Cookie heißt dbglive und benötigt den Wert 1, um Debugging zu aktivieren. Mit Eingabe von

document.cookie='dbglive=1'

in der Konsole des Browsers ist das als Session-Cookie schnell erledigt. Wer eine dauerhaftere Lösung möchte, kann dem Cookie (unter "Application - Storage - Cookies") in der Konsole eine längere Laufzeit als "Session" verpassen. Ist das erledigt, sieht man beim Neuladen einer Seite, in der das lokale Script verwendet wird, den zweiten Request für den Live-Container, wenn z. B. man nach "gtm" sucht. Und vor allem zeigen sich in der Vorschau des Tag Managers nun wieder alle dataLayer Events und Tags, die man ohne den nachgeladenen Live-Container dort nicht sehen konnte.

Nicht unwichtig: Cache Control

Im Zeitalter von Page-Speed- und Web-Vitals-Manie bei den meisten Webmastern leben lokale Ressourcen wie JavaScript Dateien eine ganze Weile, um unnötiges Nachladen von sich i. d. R. selten ändernden Dateien zu vermeiden.

Das muss für den eigenen GTM Container aber nicht die beste Option sein, wenn nach einer Veröffentlichung evtl. noch Wochen oder Monte vergehen, bis bei wiederkehrenden Besuchern der (veraltete) GTM Container im Browser verstirbt und neu abgerufen wird. Google nutzt für den GTM Container daher einen expires Header, der dem aktuellen Zeitstempel entspricht. Zudem wird ein Header gesendet, der den lokalen Cache auf 15 Minuten begrenzt: cache-control: private, max-age=900

Über die .htaccess des Servers kann das z. B. so aussehen (wie immer: Vorsicht mit Serverkonfiguration und immer eine Sicherung zur Hand haben!):

<Files gtm.js>
Header set cache-control "private, max-age=900"
ExpiresActive on
ExpiresDefault "access"
</Files>

Das muss man nicht genau so machen, aber eine separate Konfiguration für das Caching des lokalen GTM Containers ist eine sinnvolle Maßnahme. Als Notbehelf kann optional bei einer neuen GTM Fassung die Versionsnummer im einbindenden Script verändert werden (siehe oben), was ein Neuladen ebenso erzwingt.

Vollständige Automatisierung

Der oben beschriebene Prozess braucht immer noch einen Menschen, der nach der Veröffentlichung den Cache durch Aufruf der PHP Datei aktualisiert. Das ist zwar nicht besonders aufwändig, aber es ist ein potentielles Loch im Deployment-Prozess. Dies kann auf verschiedene Weise gelöst werden. Ein exotischer Ansatz wäre z. B. der regelmäßige Check des Live Containers in einer Cloud Function durch Abruf des Containers oder gar via API, die die Aktualisierung bei Neuerungen anwirft... oder einfach durch eine E-Mail getriggert wird.

Denn da der Tag Manager zum Glück bei neuen Versionen auf Wunsch in den Benachrichtigungs-Einstellungen auch eine Mail senden kann, wann immer eine neue Version live geht, sind auch einfachere Wege vorhanden. Wird eine eingehende Mail-Benachrichtigung z. B. vom Mail Server per Regel weitergeleitet an eine eigene Funktion zur Aktualisierung in der Cloud. Auch ausgelesen mit einem E-Mail-Trigger bei make.com oder ähnlichen Diensten wie Zapier, IFTTT oder ähnliche Dienste mit E-Mail Triggern (Danke an Michael Janssen für den Tipp), kann darüber (ggf. mit einigen Minuten Verzögerung) der Aktualisierungsservice getriggert werden, wenn man über die Hürde springen kann, den Usernamen und das Passwort des htaccess-Schutzes mit in der URL dort anzugeben (Format: https://username:password@www.meinedomain.de/ordner/getgtm.php).

Fazit: Für wen lohnt sich der Aufwand?

Nicht jeder wird sich die Mühe machen wollen, einen lokalen Container zu hosten. Das ist in Ordnung - oft wird ein GTM selbst je nach eingesetztem Consent Tool blockiert oder gar nicht erst geladen. Ist aber ein GTM so verbaut, dass er unabhängig von Zustimmung immer ungehindert geladen wird, ist angesichts des sich abzeichnenden schweren Stands für externe Ressourcen von Google Servern im Speziellen das Ausweichen auf einen lokalen Container eine sehr gute Idee.

Nicht gelöst ist damit das Nachladen von Google-Tracking-Scripts oder der Versand von Tracking-Hits direkt aus dem Browser an Google Endpunkte. Wer hier ansetzen will oder muss, wird an einem eigenen Endpunkt nicht vorbeikommen.

Hat Dir der Beitrag geholfen?

Dann freue ich mich, wenn Du ihn mit anderen teilst! Wenn Du magst, gib einen aus ;)

© 2001 - 2024 Markus Baersch