Geschrieben von

Google Ad Manager Tagless Request

WebDev

Standardmäßig werden Anzeigen vom Google Ad Manager-Server über die Google Publisher Tags (GPT) geladen. Dabei wird die GPT-Bibliothek über den Head-Bereich implementiert, worüber dann weitere Requests angestoßen werden, um alle Komponenten der Bibliothek zu laden. Anschließend werden die entsprechenden Methoden in das googletag-Objekt gepusht und die Google Publisher Tags können daraufhin ihre Arbeit leiten, die Requests bauen, Requests durchführen und die angefragte Anzeige ausspielen.

Es gibt jedoch Fälle und Umgebungen, wo das Tag nicht eingesetzt werden kann. Hier lässt sich dann eine Anfrage ohne Tags durchführen, um das Creative anzufordern. Das geschieht über den sogenannten Google Ad Manager Tagless Request. Die offizielle Dokumentation darüber gibt es im Google Ad Manager Helpcenter. Wie man ein Creative ohne Google Publisher Tags bzw. Anzeigen-Tags anfragen kann, zeige ich in diesem Beitrag. Folgende Themen werde ich behandeln:

Google Ad Manager Tagless Request durchführen

Um das Creative ohne Anzeigen-Tags anzufordern brauchen wir einige Informationen aus der oben verlinkten Dokumentation:

  • Der Endpunkt, wo wir das Creative beim Ad-Server anfragen können lautet https://securepubads.g.doubleclick.net/gampad/adx.
  • Damit die Anfrage erfolgreich verarbeitet wird, sind einige Pflicht-Parameter notwendig.

Dabei handelt es sich um folgende Parameter:

  • iu: Über diesen Parameter wird der Ad Unit-Code mitgegeben, also z.B. “/6355419/Travel”.
  • sz: Darüber kann eine oder mehrere Creative-Größen geschickt werden. Dabei muss das Format “300×250” lauten. Falls mehrere Größen mitgeschickt werden, müssen sie mit einem Pipe voneinander getrennt werden, also z.B. “300×250|728×90”.
  • url: Darüber wird die Domain, die die Anfrage sendet, gesetzt. Dieser Parameter ist nur für das Publisher-Verwaltungstool relevant.
  • c: Über den Parameter “c” sollte ein Cache-Busting-Wert geschickt werden, damit die Anfrage nicht gecached wird.
  • tile: Falls auf der Seite mehrere Anfragen ohne Anzeigen-Tags stattfinden, dann wird über “tile” die Reihenfolge der Anfrage spezifiziert. Am besten aufsteigend sortiert. Die erste Anfrage kann z.B. den Wert tile=1 schicken, die Zweite tile=2, usw.

Es können noch weitere optionale Parameter bei Bedarf mitgeschickt werden. Welche das sind und ob einige davon für dich relevant sind, kannst du in der oben verlinkten Dokumentation nachsehen. Die Anfrage mit den Pflicht-Parametern liefert im Response ein HTML zurück, also nicht direkt das Creative! Daher muss entsprechend im Browser die Anfrage geparst und gerendert werden. Liegt das zurück gelieferte HTML im Browser vor, enthält es ein img-Tag mit einem Request zum Creative. Erst dann kann das Creative auf der Website angezeigt werden. Das ist wichtig zu verstehen, da man sonst keine Anzeige ausgeliefert bekommt. Außerdem: Da der Tagless Request ein HTML zurückliefert, sollte dieses innerhalb eines iFrames gerendert werden, damit es nicht die Haupt-Seite negativ beeinflusst.

Entsprechend starten wir damit, an der Stelle im HTML wo die Anzeige erscheinen soll, ein iFrame zu erstellen:

<iframe src="iframe.html" width="300" height="250" scrolling="no" style="border:none" sandbox="allow-scripts">
</iframe>

Ich verweise damit auf das iFrame mit dem Dateiname “iframe.html”. Die restlichen HTML-Attribute können nach Bedarf gesetzt werden. Im nächsten Schritt brauchen wir also unsere iframe.html-Seite, die zunächst wie folgt aussehen kann:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Google Ad Manager - Tagless Request</title>
</head>
<body>
    <div id="tagless-request"></div>
</body>
</html>

Bis auf ein paar Standard-HTML-Angaben und einen div-Container, wo die Antwort des Requests reinkommt, ist sonst nichts definiert. Nun kann die JavaScript-Logik initialisiert werden. Zunächst werden wichtige Daten gesetzt. Passe “[BITTE ANPASSEN]” mit deinen Daten an. Unter “adUnitCode” kommt der Ad Unit-Code, unter “creativeSize” die anzufragenden Creative-Größen und unter “adHost” deine Domain:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Google Ad Manager - Tagless Request</title>
</head>
<body>
    <div id="tagless-request"></div>

    <script>
        const adUnitCode = '[BITTE ANPASSEN]', // Hier kommt der Ad Unit-Code
              creativeSize = '[BITTE ANPASSEN]', // Hier kommen die Creative-Größen
              adHost = '[BITTE ANPASSEN]'; // Platziere hier deine Domain
    </script>
</body>
</html>

Hier ein Beispiel wie das aussehen kann:

const adUnitCode = '/6355419/Travel/Europe/France/Paris', // Hier kommt der Ad Unit-Code
      creativeSize = '300x250|728x90', // Hier kommen die Creative-Größen
      adHost = 'www.example.com'; // Platziere hier deine Domain

Danach folgen noch weitere wichtige Variablen und die fetch-Logik, um einen GET-Request anzustoßen und den Response zu verarbeiten:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Inside Frame</title>
</head>
<body>

    <div id="tagless-request"></div>

    <script>
        const adUnitCode = '/6355419/Travel/Europe/France/Paris',
              creativeSize = '300x250|728x90',
              adHost = 'www.example.com';

        const trEndpoint = 'https://securepubads.g.doubleclick.net/gampad/adx?',
              cacheBust = Math.round(new Date().getTime() / 1000);

        fetch(trEndpoint + 'iu=' + adUnitCode + '&sz=' + encodeURI(creativeSize) + '&url=' + adHost + '&c=' + cacheBust + '&tile=1')
        .then((response) => {
            return response.text();  
        })
        .then((text) => {
            document.getElementById("tagless-request").innerHTML = text;  
        })  
      </script>

</body>
</html>

Dabei wird die Antwort in den vorher definierten div-Container mit der ID “tagless-request” gesetzt. Der Code ruft dann das Creative von https://tpc.googlesyndication.com/simgad/ auf und schickt es an den Browser zurück:

Creative ohne Tag anfordern inkl. Impression-Tracking

Mit der soeben beschriebenen Methode lässt sich zwar das Creative ohne GPT-Tags anzeigen, aber es findet leider keine Impression-Zählung statt. Um eine Impression zu zählen müssen der Request-URL die zwei Parameter “d_imp=1” und “d_imp_hdr=1” mitgegeben werden. Damit erhält man dann in der Antwort den Header “google-delayed-impression” zurück. Darin ist die URL enthalten, die man “anpingen” muss, um eine Impression für das Creative zu zählen.

Mit dem oben beschriebenen Code haben wir jedoch das Problem, dass wir über die fetch-Methode auf diesen Header im Response nicht zugreifen können. Über fetch() oder auch XMLHttpRequest() kann man nur auf bestimmte Standard-Header clientseitig zugreifen:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Daher müssen wir serverseitig auf den gewünschten Header zugreifen. Mit einer kleinen NodeJS-App würde das z.B. gehen. Dazu passen wir zunächst unsere Abfrage im iFrame so an, dass sie zu einen beliebigen Pfad zur App geht (hier am Beispiel mit einem localhost-Server):

<div id="tagless-request"></div>
    <script>
        fetch('http://localhost:8080/get-ad')
          .then((response) => {
            return response.text();
          })
          .then((text) => {
            document.getElementById("tagless-request").innerHTML = text;  
          });
    </script>

Diese fetch-Anfrage führt einen Request auf dem eigenen Server aus, der noch verarbeitet werden muss.

Auf meinem NodeJS-Server brauche ich zunächst das node-fetch-Paket, welche sich mit folgender Anweisung installiere:

npm install node-fetch@2

Version 2 ist in diesem Fall wichtig, damit es mit CommonJS kompatibel ist. Auf meiner Server-App kann ich nun die notwendigen APIs laden:

const express = require('express');
const path = require('path');
const fetch = require('node-fetch');

const app = express();

Dann brauche ich eine Funktion, die die URL aus dem Header “google-delayed-impression” als Parameter entgegennimmt und sie anpingt:

const gamImpression = (requestUrl) => {
    fetch(requestUrl)
        .then((impressionRes) => {
            console.log(impressionRes);
        })
    console.log("Impression-Tracking: Ping done")
};

Dann folgt eine Funktion, die den initialen Request abschickt, um das Creative anzufordern:

const taglessRequest = () => {
    const trEndpoint = 'https://securepubads.g.doubleclick.net/gampad/adx?',
          adUnitCode = '/6355419/Travel/Europe/France/Paris',
          creativeSize = '300x250',
          adHost = 'www.example.com',
          cacheBust = Math.round(new Date().getTime() / 1000);

    let url = trEndpoint + 'iu=' + adUnitCode + '&sz=' + creativeSize + '&url=' + adHost + '&c=' + cacheBust + '&tile=1&d_imp=1&d_imp_hdr=1';
    return url;
};

Dabei sind die Parameter “d_imp=1&d_imp_hdr=1” besonders wichtig, da nur durch sie die anzupingende URL im Response-Header zurückgegeben wird. Den Kern bildet nun die Annahme des Requests aus dem iFrame (/get-ad) und die Verarbeitung der Anfrage:

app.get('/get-ad', (req, res) => {
    const url = taglessRequest();

    fetch(url).then((adResponse) => {
        const impressionPing = adResponse.headers.get('google-delayed-impression');
        gamImpression(impressionPing);
        return adResponse.text();
    })
    .then((adText) => {
        res.send(adText);
    })
});

Zunächst wird die URL für den Tagless Request in der Variable “url” gespeichert, die dann der fetch-Methode übergeben wird. Danach wird über folgender Anweisung auf den entsprechenden Response-Header zugegriffen:

adResponse.headers.get('google-delayed-impression');

Die Variable, in der der Wert gespeichert wird, wird dann an die Funktion, die den Ping rausschickt, übergeben:

gamImpression(impressionPing);

Die ganze App kann wie folgt aussehen:

const express = require('express');
const path = require('path');
const fetch = require('node-fetch');

const app = express();

const gamImpression = (requestUrl) => {
    fetch(requestUrl)
        .then((impressionRes) => {
            console.log(impressionRes);
        })
    console.log("Impression-Tracking: Ping done")
};

const taglessRequest = () => {
    const trEndpoint = 'https://securepubads.g.doubleclick.net/gampad/adx?',
          adUnitCode = '/6355419/Travel/Europe/France/Paris',
          creativeSize = '300x250',
          adHost = 'www.example.com',
          cacheBust = Math.round(new Date().getTime() / 1000);

    let url = trEndpoint + 'iu=' + adUnitCode + '&sz=' + creativeSize + '&url=' + adHost + '&c=' + cacheBust + '&tile=1&d_imp=1&d_imp_hdr=1';
    return url;
};

app.use('/assets', express.static('assets'));
app.use('/img', express.static('img'));

app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname) + '/views/index.html');
});

app.get('/iframe.html', (req, res) => {
    res.sendFile(path.join(__dirname) + '/views/iframe.html');
});

app.get('/get-ad', (req, res) => {
    const url = taglessRequest();

    fetch(url).then((adResponse) => {
        const impressionPing = adResponse.headers.get('google-delayed-impression');
        gamImpression(impressionPing);
        return adResponse.text();
    })
    .then((adText) => {
        res.send(adText);
    })
});

const PORT = process.env.PORT || 8080;
app.listen(PORT, _ => {
    console.log(`App deployed at Port ${PORT}`);
});

Mit “node server.js” kann ich nun den Server starten, der auf meinem definierten Port dann läuft:

Rufe ich nun http://localhost:8080/ auf, wird die Ad entsprechend ausgeliefert:

Über die console.log()-Aufrufe in der App, kann ich z.B. sehen, dass meine Funktion erfolgreich den Impression-Ping rausgeschickt hat:

Gleichzeitig logge ich die Response vom Impression-Ping und kann sehen, dass ein 200er zurückgeliefert wird:

Die Code-Beispiel gibt es auch in meiner GitHub-Repo.

Last modified: 9. Januar 2023