{"id":84481,"date":"2025-02-12T19:42:38","date_gmt":"2025-02-12T19:42:38","guid":{"rendered":"https:\/\/peraltafinancing.com\/analytics\/server-side-tagging-in-google-tag-manager\/"},"modified":"2025-02-12T19:42:38","modified_gmt":"2025-02-12T19:42:38","slug":"server-side-tagging-in-google-tag-manager","status":"publish","type":"post","link":"https:\/\/fivemor.com\/?p=84481","title":{"rendered":"Server-side Tagging In Google Tag Manager"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>Ever since <strong>Server-side tagging<\/strong> was <a href=\"https:\/\/twitter.com\/SimoAhava\/status\/1222459714614841346?s=20\">publicly announced<\/a> at <a href=\"https:\/\/superweek.hu\/\">SUPERWEEK 2020<\/a>, Google and the trusted tester community have been hard at work, building something that just might change the landscape of digital analytics for good.<\/p>\n<p><a href=\"https:\/\/tagmanager.google.com\/\">Google Tag Manager<\/a> has now released Server-side tagging into <strong>public beta<\/strong>. In this lengthy article, we\u2019ll take a look at what Server-side tagging is, how it should (and should not) be used, and what its implications are on the broader digital analytics community.<\/p>\n<div style=\"aspect-ratio: 2764 \/ 1390;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/server-side-tagging-google-tag-manager.jpg\" title=\"Server-side tagging\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1390\" width=\"2764\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/server-side-tagging-google-tag-manager.jpg#ZgotmplZ\" alt=\"Server-side tagging\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In short, <strong>Server-side tagging<\/strong> means running a <strong>Google Tag Manager container<\/strong> in a server-side environment (at the time of writing, the only available environment is the <a href=\"https:\/\/cloud.google.com\/\">Google Cloud Platform<\/a>, though I\u2019m certain more options will become available in good time).<\/p>\n<p>Many of the <a href=\"#key-benefits\">benefits<\/a> and <a href=\"#key-concerns\">concerns<\/a> are tackled in their respective chapters. Even so, I want to emphasize that Server-side tagging has the potential to overturn the current dynamic of data collection and governance for an organization. You <a href=\"#full-control-and-ownership-of-the-data-collected-by-the-container\"><strong>own<\/strong> and have <strong>full control<\/strong><\/a> over the server-side environment. You have access to <strong>tools<\/strong> and <strong>methods<\/strong> to thoroughly <a href=\"#clean-up-and-validate-payloads\">vet and validate the traffic<\/a> between network sources and your advertising and analytics endpoints.<\/p>\n<p>You can run a fully functional digital analytics and marketing setup without loading <em>any<\/em> third-party code in the user\u2019s browser or device. With appropriate monitoring in place, you can say <a href=\"#more-control-over-http-traffic\">goodbye to PII and credential leaks<\/a>, cross-site tracking traps, and bloated third-party JavaScript <a href=\"#reduced-client-load\">encumbering the client<\/a>.<\/p>\n<p>Server-side tagging utilizes many of the concepts familiar to Google Tag Manager users:<\/p>\n<ul>\n<li>\n<p>There are <strong>tags<\/strong> which fire on <strong>triggers<\/strong> and pull in data from <strong>variables<\/strong>.<\/p>\n<\/li>\n<li>\n<p>New container <strong>versions<\/strong> can be <strong>previewed<\/strong> and <strong>published<\/strong>.<\/p>\n<\/li>\n<li>\n<p>Users can create their own <a href=\"https:\/\/www.simoahava.com\/analytics\/custom-templates-guide-for-google-tag-manager\/\"><strong>custom templates<\/strong><\/a>.<\/p>\n<\/li>\n<\/ul>\n<p>However, there are new, fundamentally different features that introduce something of a <strong>paradigm shift<\/strong> to the type of dynamic tagging that Google Tag Manager promotes.<\/p>\n<ul>\n<li>\n<p>The container itself is a new <a href=\"#server-container\"><strong>Server<\/strong><\/a> type; different from the web, app, and AMP containers that precede it.<\/p>\n<\/li>\n<li>\n<p>Instead of trigger events, processes are initialized by <strong>incoming HTTP requests<\/strong>.<\/p>\n<\/li>\n<li>\n<p>These requests are digested by a new type of GTM entity: <strong>a <a href=\"#clients-and-tags\">Client<\/a><\/strong>.<\/p>\n<\/li>\n<li>\n<p>The Client parses the requests, generates an <strong>event data object<\/strong>, and feeds this into a <strong>virtual container<\/strong>, where tags can use this event object to map and send data to their endpoints.<\/p>\n<\/li>\n<\/ul>\n<div style=\"aspect-ratio: 1948 \/ 866;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/08\/preview-example.jpg\" title=\"Example from preview mode\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"866\" width=\"1948\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/08\/preview-example.jpg#ZgotmplZ\" alt=\"Example from preview mode\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>This article <strong>will not be<\/strong> an exhaustive guide. I will walk you through the main concepts of Server-side tagging and there should be little you\u2019ll be left wanting, but to complement this article, I do recommend you consult Google\u2019s <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\">own, official documentation<\/a>.<\/p>\n<p>                <span class=\"simmer\"><br \/>\n  <span class=\"close\">X<\/span><\/p>\n<p>\n    <span class=\"fa fa-md fa-bell\"\/><br \/>\n    <strong>The Simmer Newsletter<\/strong>\n  <\/p>\n<p>\n    Subscribe to the <a href=\"https:\/\/www.simoahava.com\/newsletter\/\">Simmer newsletter<\/a> to get the latest news and content from Simo Ahava into your email inbox!\n  <\/p>\n<p>  <\/span><\/p>\n<h2 id=\"how-to-follow-this-guide\">How to follow this guide<\/h2>\n<p>While I hope everyone would devour every last word of this article, I\u2019m aware that not all sections are relevant to all readers.<\/p>\n<p>If you\u2019re looking for an overview of Server-side tagging, perhaps for getting buy-in within your organization, I recommend reading these chapters:<\/p>\n<p>If you\u2019re a developer or working in IT, I\u2019d recommend focusing on these chapters:<\/p>\n<p>Everything else is still important, but I\u2019ll forgive you if you gloss over them initially, only to return to them hungrily once you\u2019re hooked into all the awesomeness that Server-side tagging brings in its wake.<\/p>\n<p>I recommend you watch the following two videos regardless.<\/p>\n<p>The first one is a general introduction to <strong>Server-side tagging<\/strong>, focusing on deployment and getting started with your first Client and tag.<\/p>\n<p>The second is a deep-dive into building your own Client template. It\u2019s a bit more specialized and can thus be skipped if you\u2019re not interested in customizing the container.<\/p>\n<h3 id=\"video-introduction-to-server-side-tagging\">Video: Introduction to Server-side tagging<\/h3>\n<p>\n  <iframe src=\"https:\/\/www.youtube-nocookie.com\/embed\/6OGbOh216mU?enablejsapi=1\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" allowfullscreen=\"\" title=\"YouTube Video\"><\/iframe>\n<\/p>\n<p>If the video doesn\u2019t work, you can watch it <a href=\"https:\/\/youtu.be\/6OGbOh216mU\">here<\/a>.<\/p>\n<h3 id=\"video-create-a-client-template-in-a-server-container\">Video: Create a Client template in a Server container<\/h3>\n<blockquote>\n<p><strong>NOTE!<\/strong> The video below has one important omission. When creating the <strong>Client<\/strong> template, make sure to update the \u201cSends HTTP Requests\u201d permission to include \u201cAllow Google Domains\u201d. Otherwise the proxying of analytics.js doesn\u2019t work.<\/p>\n<\/blockquote>\n<p>\n  <iframe src=\"https:\/\/www.youtube-nocookie.com\/embed\/_c4JEfSkP6U?enablejsapi=1\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" allowfullscreen=\"\" title=\"YouTube Video\"><\/iframe>\n<\/p>\n<p>If the video doesn\u2019t work, you can watch it <a href=\"https:\/\/youtu.be\/_c4JEfSkP6U\">here<\/a>.<\/p>\n<h2 id=\"what-is-server-side-tagging\">What is Server-side tagging?<\/h2>\n<div style=\"aspect-ratio: 2590 \/ 1472;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/server-side-tagging-outline.jpg\" title=\"Server-side tagging outline\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1472\" width=\"2590\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/server-side-tagging-outline.jpg#ZgotmplZ\" alt=\"Server-side tagging outline\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>With Server-side tagging, Google Tag Manager has introduced a new <strong>Server<\/strong> container type, which resides in a <a href=\"https:\/\/cloud.google.com\/\">Google Cloud<\/a> environment.<\/p>\n<p>In a nutshell, the purpose of this setup is to create an endpoint in a server environment that <strong>you own<\/strong>. It will act as a sort of a <strong>proxy<\/strong> between the hits sent from browsers and devices and the actual endpoints to which the hits are collected. See the <a href=\"#key-benefits\">next chapter<\/a> for more details on what this type of proxy can do.<\/p>\n<p>The container itself operates as an HTTP API endpoint, to which any browser, device, or other sources that support the HTTP protocol can send requests.<\/p>\n<p>Ideally, this endpoint would be mapped with a <strong>custom subdomain<\/strong> in the same domain hierarchy as the website sending the requests. That way the requests are considered to happen in <a href=\"https:\/\/www.cookiestatus.com\/introduction\/tracking-protection\/#first-party-and-third-party-context\">first-party context<\/a>, which has a significant impact on how cookies can be read and written, for example.<\/p>\n<p>Within the Server container, workers known as <strong>Clients<\/strong> are configured to listen for these incoming HTTP requests, which they then parse into a <strong>unified event format<\/strong>. The Clients then run a <em>virtual container<\/em> with the <a href=\"#event-model\">event data object<\/a>, where tags, triggers, and variables react to the event push similar to how they would with \u201cregular\u201d Google Tag Manager.<\/p>\n<p>Tags take the information in these event data objects and compile them into HTTP requests to their respective endpoints. Finally, the Client sends an HTTP response back to the source of the initial request.<\/p>\n<div style=\"aspect-ratio: 2760 \/ 738;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/ua-response.jpg\" title=\"Example of a Universal Analytics client responding with a success status and setting the _ga cookie in the response.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"738\" width=\"2760\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/ua-response.jpg#ZgotmplZ\" alt=\"Example of a Universal Analytics client responding with a success status and setting the _ga cookie in the response.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Example of a Universal Analytics client responding with a success status and setting the _ga cookie in the response.<\/span><\/p>\n<\/div>\n<p>All of the above happens within the confines of the Server-side tagging environment. The only way the browser or app sending the data can be made aware of what\u2019s going on is if the <strong>Client<\/strong> adds information into the HTTP response, which is fully configurable.<\/p>\n<h2 id=\"key-benefits\">Key benefits<\/h2>\n<p>Here are some of the key benefits of using Server-side tagging.<\/p>\n<h3 id=\"reduced-client-load\">Reduced client load<\/h3>\n<p>By running the logic of building and dispatching hits to the vendor endpoint in your server-side environment, you have a golden opportunity to reduce the amount of (especially third-party) JavaScript run in the user\u2019s browser.<\/p>\n<p>Because you can configure the Server container to map <em>any<\/em> incoming HTTP request into the format required by the vendor, you can theoretically reduce your entire third-party pixel and JavaScript load to a single event stream directed into your Server container.<\/p>\n<div style=\"aspect-ratio: 2014 \/ 940;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/all-javascript.jpg\" title=\"Imagine if you could reduce the amount of JavaScript loaded and executed in the browser...\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"940\" width=\"2014\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/all-javascript.jpg#ZgotmplZ\" alt=\"Imagine if you could reduce the amount of JavaScript loaded and executed in the browser...\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Imagine if you could reduce the amount of JavaScript loaded and executed in the browser&#8230;<\/span><\/p>\n<\/div>\n<p>This stream can then be intercepted by a Client which proceeds to map the stream into the <a href=\"#event-model\">event model<\/a> expected by the vendor tags, also running in the Server container.<\/p>\n<p>This is the <strong>ultimate benefit<\/strong> of a Server-side tagging setup. Even if you don\u2019t want to reduce everything to a single stream, you can build your own <a href=\"https:\/\/www.simoahava.com\/analytics\/custom-templates-guide-for-google-tag-manager\/\">custom template<\/a> in the web container, which builds the HTTP request to the Server container without having to load any third-party JavaScript at all (apart from the GTM library itself).<\/p>\n<p><!--\nThus you could have a Facebook Pixel (facebook-pixel-server-side.md) template running in the web container which doesn't even load anything from Facebook's servers! All it does is gather [the information required](https:\/\/developers.facebook.com\/docs\/marketing-api\/conversions-api) for the Server container to map this HTTP request (facebook-client.md) into Facebook's format.\n--><\/p>\n<h3 id=\"keep-keys-and-secrets-safe\">Keep keys and secrets safe<\/h3>\n<p>By transporting data processing logic away from the device, where it would be visible for anyone with debugging skills, you will also be able to run secured and credential-based transactions without having to worry about exposing sensitive information to the device.<\/p>\n<p>For example, a plague on Google Analytics has been <a href=\"https:\/\/help.analyticsedge.com\/article\/definitive-guide-to-removing-google-analytics-spam\/\">Measurement Protocol spam<\/a>, where malicious parties crawl potential tracking IDs and then proceed to spam them with automated HTTP requests that masquerade as \u201cregular\u201d hits from the site. Alternatively, these hackers send spam hits to <strong>random tracking IDs<\/strong>, knowing that if they send enough hits, some of them will end up in real Universal Analytics accounts.<\/p>\n<p>This type of spam is notoriously difficult to identify and prevent because it\u2019s built to resemble actual hits that are sent from the website itself.<\/p>\n<p>Now that you have the server endpoint handy, you can add a new <strong>Custom Dimension<\/strong> within the Server container, which is then sent to Google Analytics. In Google Analytics, you can then create a filter for this Custom Dimension, allowing only traffic that matches it.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\">event[<span style=\"color:#a50\">'x-ga-mp1-cd11'<\/span>] = <span style=\"color:#a50\">'my_secret_key'<\/span>;\n<\/code><\/pre>\n<\/div>\n<div style=\"aspect-ratio: 1676 \/ 1124;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/custom-dimension-set.jpg\" title=\"Secret key custom dimension\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1124\" width=\"1676\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/custom-dimension-set.jpg#ZgotmplZ\" alt=\"Secret key custom dimension\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>By adding this \u201csecret key\u201d in the Server container, there\u2019s no way that a random Measurement Protocol spammer can know it\u2019s there. Similarly, it won\u2019t help if the spammer crawls your site, looking at the requests sent to Google Analytics, because there are no such requests! There are only requests to your own server-side endpoint, and it would be odd if Measurement Protocol spammers would utilize those to fuel their spam algorithms.<\/p>\n<p>Naturally, this isn\u2019t limited to just what you can do with Universal Analytics. Any third-party servers that identify your access with an <strong>API key<\/strong> or <strong>credential token<\/strong> can now be proxied through your Server container so that these keys are not exposed in the device!<\/p>\n<h3 id=\"more-control-over-what-endpoints-collect\">More control over what endpoints collect<\/h3>\n<p>Because your proxy now resides between the user\u2019s device and the endpoint, you are in full control over what is shipped to the vendor.<\/p>\n<p>Unless the Client <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#getremoteaddress\">specifically overrides<\/a> things like the IP address and User-Agent in the outgoing HTTP request from the Server container (this is what the built-in Universal Analytics client does by default), the IP and User-Agent string will be that of your Server container rather than the user. So this is a great way to concretely anonymize this aspect of the HTTP protocol that\u2019s proven to be problematic in terms of end-user privacy.<\/p>\n<div style=\"aspect-ratio: 1552 \/ 436;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/override-ip.jpg\" title=\"IP address and User-Agent string overridden in a Google Analytics request.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"436\" width=\"1552\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/override-ip.jpg#ZgotmplZ\" alt=\"IP address and User-Agent string overridden in a Google Analytics request.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">IP address and User-Agent string overridden in a Google Analytics request.<\/span><\/p>\n<\/div>\n<p>Server-side tagging introduces extra control over privacy simply by existing.<\/p>\n<p>Without manual overrides, the requests sent to the vendors are mapped to the App Engine virtual machine instead of the user\u2019s browser or device.<\/p>\n<p>There are no <strong>data leaks<\/strong> with third-party cookies, there are no surprises with <strong>injected URL parameters<\/strong>, and the third-party service doesn\u2019t have <em>any<\/em> connection with the user\u2019s browser by default. They\u2019ll be communicating just with the cloud machine.<\/p>\n<h3 id=\"more-control-over-http-traffic\">More control over HTTP traffic<\/h3>\n<p>To expand the features mentioned above, you will also have full control over what HTTP traffic is passed through the Server container.<\/p>\n<p>Typically, the browser loads the vendor\u2019s JavaScript from their <strong>content distribution network<\/strong> (CDN).<\/p>\n<p>This act already exposes the user\u2019s browser or device to the third party and can lead to things like personally identifiable information <strong>(PII) leaks<\/strong> in case the URL of the page has sensitive information.<\/p>\n<div style=\"aspect-ratio: 1984 \/ 92;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/pii-referrer.jpg\" title=\"It's better to leak this to your data store rather than a vendor's.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"92\" width=\"1984\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/pii-referrer.jpg#ZgotmplZ\" alt=\"It's better to leak this to your data store rather than a vendor's.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">It&#8217;s better to leak this to your data store rather than a vendor&#8217;s.<\/span><\/p>\n<\/div>\n<p>Because you now have a proxy between the device and the endpoint, the only place where this information is leaked is into <strong>your<\/strong> cloud environment.<\/p>\n<p>Sure, it\u2019s still not optimal &#8211; <strong>PII leaks should be eradicated<\/strong>.<\/p>\n<p>But you have full control and ownership of all the data collected by the Server container, and you also have all the tools at your disposal to <a href=\"#clean-up-and-validate-payloads\">clean up and validate the payloads<\/a>.<\/p>\n<p>You can also cover your legal back by removing <strong>fingerprintable<\/strong> surfaces from the outgoing requests from the Server container. Similarly, you can use the <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#sha256\">APIs available<\/a> to <strong>hash<\/strong> potentially sensitive data. You can also, of course, look for <strong>consent strings<\/strong> in the user\u2019s cookies (assuming the Server container is in first-party context with the source of the traffic) and act accordingly.<\/p>\n<p>Finally, in the Client, you can modify the <strong>HTTP response<\/strong> back from the Server container to the browser or device. This is a pretty cool thing when considering <a href=\"https:\/\/www.cookiestatus.com\/safari\/\">Apple\u2019s Intelligent Tracking Prevention<\/a>, for example. You can convert cookies written with JavaScript, and thus subject to an expiration limit of 7 days, into HTTP cookies written with a <code>Set-Cookie<\/code> header, thus extending their lifetime to whatever you choose:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#00a\">const<\/span> getCookie = require(<span style=\"color:#a50\">'getCookieValues'<\/span>);\n<span style=\"color:#00a\">const<\/span> setCookie = require(<span style=\"color:#a50\">'setCookie'<\/span>);\n\n<span style=\"color:#aaa;font-style:italic\">\/\/ Get cookie from the HTTP request\n<\/span><span style=\"color:#aaa;font-style:italic\"\/><span style=\"color:#00a\">let<\/span> ga = getCookie(<span style=\"color:#a50\">'_ga'<\/span>);\n\n<span style=\"color:#aaa;font-style:italic\">\/\/ If no cookie exists, generate a new Client ID\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>ga = ga &amp;&amp; ga.length ? ga[<span style=\"color:#099\">0<\/span>] : generateClientId();\n\n<span style=\"color:#aaa;font-style:italic\">\/\/ Write the _ga cookie in the HTTP response\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>setCookie(<span style=\"color:#a50\">'_ga'<\/span>, ga, {\n  domain: <span style=\"color:#a50\">'auto'<\/span>,\n  <span style=\"color:#a50\">'max-age'<\/span>: <span style=\"color:#099\">63072000<\/span>,\n  path: <span style=\"color:#a50\">'\/'<\/span>,\n  secure: <span style=\"color:#00a\">true<\/span>,\n  sameSite: <span style=\"color:#a50\">'lax'<\/span>\n});\n<\/code><\/pre>\n<\/div>\n<p><em>Ideally<\/em>, you\u2019ll want to set cookies with the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Cookies#Security\"><code>HttpOnly<\/code><\/a> flag. This prevents the web page from accessing the cookie with JavaScript (<code>document.cookie<\/code>). By allowing cookie access only for the webserver receiving the request, you\u2019re introducing a decent redundancy measure for mitigating <strong>cross-site scripting<\/strong> attacks and preventing cookie values from leaking into <a href=\"https:\/\/www.cookiestatus.com\/introduction\/tracking-protection\/#cross-site-tracking\">cross-site tracking<\/a> schemes.<\/p>\n<p>The reason we\u2019re <em>not<\/em> using <code>HttpOnly<\/code> in the example above is because <a href=\"https:\/\/support.google.com\/analytics\/answer\/1033876?hl=en\">cross-domain linking<\/a> is something Universal Analytics still does client-side with JavaScript.<\/p>\n<blockquote>\n<p>Note! You might want to read <a href=\"https:\/\/www.simoahava.com\/analytics\/fpid-cookie-google-analytics-server-side-tagging\/\">this article on FPID<\/a> to see what Google is working on in terms of improving the security of the Google Analytics cookie.<\/p>\n<\/blockquote>\n<p>In any case, using the <code>Set-Cookie<\/code> header like this removes the need for <a href=\"https:\/\/www.simoahava.com\/google-cloud\/create-cookie-rewrite-web-service-google-cloud\/\">complicated APIs to do the cookie rewriting for you<\/a>, as you can just piggy-back the cookie rewrite on the data collection itself.<\/p>\n<h3 id=\"content-security-policy\">Content Security Policy<\/h3>\n<p>Another benefit of reducing the number of HTTP endpoints with which the browser communicates concerns your site\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/CSP\">Content Security Policy<\/a> (CSP). A CSP is what your site would use to restrict the HTTP traffic to and from the user\u2019s browser.<\/p>\n<p>For example, if you add a JavaScript library that loads its content from Facebook to a site with a CSP, you\u2019ll need to petition the developers to relax that CSP so that Facebook\u2019s domains would be allowed to send and receive data from the user\u2019s browser.<\/p>\n<p>Naturally, the more you relax the CSP, the less efficient it becomes.<\/p>\n<div style=\"aspect-ratio: 4056 \/ 232;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/content-security-policy.jpg\" title=\"CSPs tend to get really bloated and thus inefficient at doing what they're meant to do.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"232\" width=\"4056\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/content-security-policy.jpg#ZgotmplZ\" alt=\"CSPs tend to get really bloated and thus inefficient at doing what they're meant to do.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">CSPs tend to get really bloated and thus inefficient at doing what they&#8217;re meant to do.<\/span><\/p>\n<\/div>\n<p>By reducing the number of HTTP endpoints the browser needs to communicate with (because you\u2019ve replaced them with your own Server-side tagging endpoint), you\u2019re making the CSP more robust as a result.<\/p>\n<h3 id=\"clean-up-and-validate-payloads\">Clean up and validate payloads<\/h3>\n<p>Even if you\u2019ve managed to clear the HTTP traffic itself of all potentially harmful information, you might still be left with <strong>URL parameters<\/strong> that the vendor requires you to populate. Sometimes, often even, these parameters contain PII, and you\u2019ll want to figure out a way to get rid of it.<\/p>\n<p>I\u2019ve written a lot about PII purging, and my <a href=\"https:\/\/www.simoahava.com\/analytics\/customtask-the-guide\/#4-remove-pii-from-ga-payloads\">customTask solution<\/a> should be useful if you\u2019re sending data from the browser directly to Google Analytics.<\/p>\n<div style=\"aspect-ratio: 1602 \/ 214;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/pii-purger.jpg\" title=\"Clear PII from requests\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"214\" width=\"1602\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/pii-purger.jpg#ZgotmplZ\" alt=\"Clear PII from requests\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>But now with a Server container, you can build a Client which parses <strong>all the request headers<\/strong> and the body looking for PII (so not just those related to Universal Analytics) and then proceeds to clean it up or obfuscate it.<\/p>\n<p>You can also use the Server container to <strong>validate<\/strong> and <strong>fix<\/strong> requests.<\/p>\n<p>For example, if you send a non-numeric value as the <strong>Event Value<\/strong> of a Universal Analytics request, that event will be <em>collected<\/em> by Google Analytics but <em>discarded<\/em> at processing. There\u2019s no warning in the browser &#8211; these hits just disappear.<\/p>\n<p>You could fix this in a Client by looking specifically for a faulty Event Value and converting it into a number:<\/p>\n<div style=\"aspect-ratio: 1334 \/ 53;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/fix-event-value.jpg\" title=\"Try to convert Event Value into a number, and default to 0 if conversion doesn't work.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"53\" width=\"1334\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/fix-event-value.jpg#ZgotmplZ\" alt=\"Try to convert Event Value into a number, and default to 0 if conversion doesn't work.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Try to convert Event Value into a number, and default to 0 if conversion doesn&#8217;t work.<\/span><\/p>\n<\/div>\n<p>You can see how this could dramatically improve your data quality once you start building Clients specifically designed for cleaning up your data streams.<\/p>\n<h3 id=\"full-control-and-ownership-of-the-data-collected-by-the-container\">Full control and ownership of the data collected by the container<\/h3>\n<p>This has already been mentioned earlier in this article, but a significant part of building a server-side environment is <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/custom-domain\">mapping a subdomain to the endpoint<\/a>. When the HTTP endpoint is able to respond to requests using a subdomain that\u2019s part of the same domain hierarchy as the website sending the requests, the website and the HTTP endpoint exist in <strong>same-site<\/strong> or <strong>first-party<\/strong> context. This has a significant impact on how <a href=\"https:\/\/www.cookiestatus.com\/\">browser tracking protections<\/a> treat the traffic.<\/p>\n<blockquote>\n<p>The custom domain <strong>should<\/strong> be mapped using A\/AAAA DNS records rather than a CNAME alias. The latter is a less useful solution for cookie permanence due to <a href=\"https:\/\/bugs.webkit.org\/show_bug.cgi?id=215201\">browser tracking protections<\/a>.<\/p>\n<\/blockquote>\n<p>Other than the question of domain context, a very important aspect of ownership is what\u2019s promised by the platform you subscribe to (<a href=\"https:\/\/cloud.google.com\">Google Cloud Platform<\/a> at the time of release).<\/p>\n<p>You have <strong>full control<\/strong> and <strong>ownership<\/strong> of the data in your Google Cloud project. Yes &#8211; you need to trust Google on this promise. Here\u2019s what they guarantee in the <a href=\"https:\/\/cloud.google.com\/security\/privacy\">relevant documentation<\/a>:<\/p>\n<ul>\n<li>\n<p>Google Cloud only processes data that you instruct it to process.<\/p>\n<\/li>\n<li>\n<p>You own your data. No data is processed by Google Cloud for advertising purposes.<\/p>\n<\/li>\n<li>\n<p>You\u2019ll always be aware of where your data is regionally located.<\/p>\n<\/li>\n<li>\n<p>Your data is secured by independently certified and audited security standards.<\/p>\n<\/li>\n<\/ul>\n<div style=\"aspect-ratio: 1984 \/ 1140;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/google-cloud-privacy.jpg\" title=\"Privacy Google Cloud\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1140\" width=\"1984\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/google-cloud-privacy.jpg#ZgotmplZ\" alt=\"Privacy Google Cloud\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>This is pretty significant. Since you own and control the data collected by the Server container, its usage and data processing falls under the privacy policies, T&amp;Cs, and contracts your organization has with its customers and end-users.<\/p>\n<p>If a data leak were to happen, for example, the first place it would \u201cleak\u201d to would be a data store that you own, and you can mitigate the fallout by making sure these leaks do not extend to the third parties to which you send the data from the Server container.<\/p>\n<p>Naturally, as soon as your Server container <em>does<\/em> fire tags that send the data to third parties, you introduce additional data processors and controllers to the mix, but having the \u201cbuffer\u201d of a data store and processor that you own in between should help a great deal in mitigating security\/privacy risks and liabilities.<\/p>\n<h3 id=\"limitless-possibilities\">Limitless possibilities<\/h3>\n<p>By shifting the processing of data to your Server-side endpoint you introduce a fairly inexpensive, scalable, and multi-functional proxy for processing data before it is wrapped up and sent to the vendors.<\/p>\n<p>In addition to the benefits listed above, there are <strong>so many<\/strong> things you could do with a server-side setup like this:<\/p>\n<ul>\n<li>\n<p>Only collect the bare essentials from the browser: content ID for content, session ID for session, and user ID for user. In the Server container, use other APIs and services at your disposal to enrich the data using these IDs as the keys.<\/p>\n<\/li>\n<li>\n<p>Run expensive code in the scalable environment of the server rather than as a burden on the user\u2019s device. Things like IP lookups and cryptographic hashing could just as well be done in the Server container.<\/p>\n<\/li>\n<li>\n<p>Maybe at some point we\u2019ll see native integrations to <strong>other<\/strong> Google Cloud products.<\/p>\n<ul>\n<li>\n<p>Imagine being able to write directly to <a href=\"https:\/\/cloud.google.com\/bigquery\">Google BigQuery<\/a> from the Server container without having to worry about complicated authentication.<\/p>\n<\/li>\n<li>\n<p>Imagine triggering <a href=\"https:\/\/cloud.google.com\/functions\">Cloud Functions<\/a> by using Pub\/Sub rather than HTTP requests.<\/p>\n<\/li>\n<li>\n<p>Imagine utilizing <a href=\"https:\/\/cloud.google.com\/logging\">Cloud logging<\/a> to build a real-time monitoring system for the integrity of your data pipeline.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Once the platform matures and once the library of available APIs and custom templates is extended, the reach of Server-side tagging is only limited by the imagination of its users.<\/p>\n<h2 id=\"key-concerns\">Key concerns<\/h2>\n<p>Moving tracking away from the device to behind the veil of the server doesn\u2019t come without its concerns.<\/p>\n<p>The paradigm shift that we can envision with GTM\u2019s Server-side tagging isn\u2019t just one of <em>improving<\/em> data collection; it\u2019s also one of <em>obfuscating<\/em> it.<\/p>\n<h3 id=\"circumvent-content-blocking\">Circumvent content blocking<\/h3>\n<p>One of the first knee-jerk reactions many probably have to Server-side tagging has to do with <strong>content blocking<\/strong> and <a href=\"https:\/\/www.cookiestatus.com\/\"><strong>browser tracking protections<\/strong><\/a> in general.<\/p>\n<p>A typical approach for privacy-friendly browsers is to restrict or downright block communications between the browser and <strong>known trackers<\/strong>. The list of <em>known trackers<\/em> is usually based on a blocklist such as <a href=\"https:\/\/disconnect.me\/\">Disconnect.Me<\/a>, but it could also be algorithmic and on-device, such as with <a href=\"https:\/\/www.cookiestatus.com\/safari\/\">Intelligent Tracking Prevention<\/a>.<\/p>\n<p>Indeed, an endpoint like <code>google-analytics.com<\/code> could well be targeted by the heuristics used in content blockers, but <code>my-server-side.domain.com<\/code> probably isn\u2019t.<\/p>\n<div style=\"aspect-ratio: 4142 \/ 360;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/content-blocking.jpg\" title=\"Direct hit to GA is blocked, but hit proxied via the Server container isn't.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"360\" width=\"4142\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/content-blocking.jpg#ZgotmplZ\" alt=\"Direct hit to GA is blocked, but hit proxied via the Server container isn't.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Direct hit to GA is blocked, but hit proxied via the Server container isn&#8217;t.<\/span><\/p>\n<\/div>\n<p>Can you use Server-side tagging to <strong>circumvent content blockers<\/strong>? Absolutely. Should you? Definitely not; at least if that\u2019s your primary reason.<\/p>\n<p>However, this does raise an interesting paradox.<\/p>\n<p><strong>It\u2019s not your fault that content blockers do not target your domains<\/strong>.<\/p>\n<p>You are not obliged to exhaustively test if all the actual endpoints are blocked by the browser. That would be a huge waste of resources and counter-productive to what Server-side tagging first and foremost does: reduce client load.<\/p>\n<p>Once server-side proxies become the norm (with a popular tool like Google Tag Manager likely to spearhead the transition), content blockers will adapt their heuristics to not just look at domains but also the information that is being sent.<\/p>\n<p>The right course of action is to be <strong>transparent<\/strong> at what data is being collected on your site, placing behind <strong>consent<\/strong> that which is required by law, and giving <strong>opt-out mechanisms<\/strong> for the rest.<\/p>\n<p>And if it just so happens that your endpoint gets blocked by content blockers or its URL string is stripped of all useful information, <strong>don\u2019t try to \u201cfix\u201d this<\/strong>.<\/p>\n<p><strong>Always err on the side of maximum privacy.<\/strong><\/p>\n<p>Always <em>assume<\/em> that the user knows exactly what they are doing when they <em>choose<\/em> to block your data collection. Don\u2019t defy their wishes.<\/p>\n<h3 id=\"opaque-data-collection\">Opaque data collection<\/h3>\n<p>When there\u2019s a data leak or a security breach in a company, the party uncovering this is often not related to the company at all.<\/p>\n<p>The web is full of companies and individuals who exhaustively audit the HTTP traffic in and out of websites, and their work has been instrumental in uncovering things like <a href=\"https:\/\/www.csoonline.com\/article\/3400381\/what-is-magecart-how-this-hacker-group-steals-payment-card-data.html\">Magecart attacks<\/a> and <a href=\"https:\/\/medium.com\/@thezedwards\/july-2020-compromised-paf-subdomains-mostly-via-microsoft-azure-5834ae22733a\">domain takeovers<\/a>.<\/p>\n<p>Cookie leaks, cross-site scripting injections, CSP workarounds, and all manner of nasty JavaScript hacks can typically be audited directly in the browser, because the vendor scripts are running right there under the watchful eyes of the auditors.<\/p>\n<p>When you move to Server-side tagging, you are reducing the amount of third-party JavaScript running on the site, <em>which is good<\/em>. It\u2019s a great step to mitigating the issues listed above.<\/p>\n<p>However, you are also removing all traces of what is actually done with the data bundled in the requests. Auditors will have a hard time deciphering just what the event stream to your endpoint actually does, and whether you are compromising the user\u2019s right to privacy and security behind the veil of the server, where client-side tools can\u2019t reach it.<\/p>\n<p>This means that you <strong>must document carefully<\/strong> what type of data is being collected and processed on your site. You are already obliged to do so under legal frameworks like GDPR and CCPA\/CPRAA, which require you to be upfront and transparent about data collection, storage, and processing.<\/p>\n<p><strong>Always err on the side of maximum privacy.<\/strong><\/p>\n<p>You should take preemptive and proactive measures to do transparency and compliance <strong>right<\/strong> in order to avoid litigation and potential brand damage when you get caught in the act.<\/p>\n<h3 id=\"consent-management-is-up-to-the-admin\">Consent management is up to the admin<\/h3>\n<p>This isn\u2019t <em>that<\/em> far removed from what the situation is currently with client-side scripts, but it\u2019s still something you need to consider.<\/p>\n<p><strong>Consent management<\/strong> is a hot topic, and rightfully so. Many sites implement client-side consent management tools, which require opt-in input from the user with regard to what data they allow to be collected from them.<\/p>\n<p>Typically the consent string is stored in a cookie or <code>localStorage<\/code> entry, and many vendors can actually proactively react to consent frameworks such as IAB\u2019s <a href=\"https:\/\/iabeurope.eu\/tcf-2-0\/\">Transparency &amp; Consent Framework 2.0<\/a>.<\/p>\n<p>When you move to Server-side tagging, you might have just a single stream of events from the browser to the server. This single stream can be split into dozens and dozens of advertising, marketing, and analytics requests in the Server container.<\/p>\n<p>The templates running in the Server container won\u2019t be able to leverage client-side consent APIs such as those suggested by TCF. Instead, you need to build the mechanism of interpreting and parsing user consent manually in the container itself.<\/p>\n<p>Possibly, and hopefully, Google will introduce tools that make this process easier. However, until then you need to make sure that <strong>when<\/strong> consent is collected in the browser or device, it is respected in the server as well.<\/p>\n<h3 id=\"cost\">Cost<\/h3>\n<p>The <strong>cost<\/strong> of running a Server-side tagging is large or small, depending on what you\u2019re comparing it to.<\/p>\n<p>It\u2019s large compared to just running the scripts in the browser, thus accumulating zero extra cost.<\/p>\n<p>It\u2019s small compared to all the <a href=\"#key-benefits\">benefits<\/a> you\u2019ll get in return for setting up the container, at least I like to think so.<\/p>\n<div style=\"aspect-ratio: 1848 \/ 482;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/forecasted-cost.jpg\" title=\"Forecasted cost with a small throughput\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"482\" width=\"1848\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/forecasted-cost.jpg#ZgotmplZ\" alt=\"Forecasted cost with a small throughput\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Forecasted cost with a small throughput<\/span><\/p>\n<\/div>\n<p>Running a three-instance App Engine setup for my <a href=\"https:\/\/www.simoahava.com\/blog-statistics\/\">site of modest traffic<\/a> puts me back about 120\u20ac per month. For me, this is acceptable considering I get more control over the data collection on my site.<\/p>\n<h3 id=\"poor-availability-of-server-side-endpoints\">Poor availability of server-side endpoints<\/h3>\n<p>For the Server container to work as a replacement for your browser- or app-based tracking, the vendors you want to send data to need to be able to collect the HTTP requests sent by the Server container.<\/p>\n<p>This isn\u2019t necessarily a big issue &#8211; vendors always have an HTTP endpoint to which their JavaScript library sends data, and many support simple image pixels for collecting the GET requests.<\/p>\n<p>However, many vendors also stuff the browser with super complicated and heavy JavaScript. If you want to work towards reducing the amount of third-party crap loaded in the browser, the vendor should provide a means to build the payload manually, without having to load their bloated JavaScript libraries.<\/p>\n<div style=\"aspect-ratio: 1042 \/ 522;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/fbevents-js.jpg\" title=\"Facebook JavaScript\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"522\" width=\"1042\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/fbevents-js.jpg#ZgotmplZ\" alt=\"Facebook JavaScript\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>For example, <strong>Facebook<\/strong> has a large and very complex set of client-side libraries they want you to download when working with their pixel. The purpose of the library is to let you communicate with Facebook\u2019s servers using the <code>fbq()<\/code> command queue.<\/p>\n<p>Luckily, Facebook <em>also<\/em> offers the <a href=\"https:\/\/developers.facebook.com\/docs\/marketing-api\/conversions-api\">Conversions API<\/a>, which specifies a format for the HTTP request with the conversion information. By deciphering the Conversions API documentation, anyone can build their own event stream from the device to the Server container to Facebook without having to load <em>any<\/em> Facebook JavaScript at all.<\/p>\n<p>Then there are services like <strong>HotJar<\/strong> that are so tightly coupled with client-side interactions that it is unlikely you can ever run HotJar without having to download their JavaScript. It will be interesting to see how vendors like HotJar adapt to a tagging environment that is run completely in the server.<\/p>\n<h2 id=\"technical-outline\">Technical outline<\/h2>\n<p>When you follow <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside#create_a_google_cloud_platform_gcp_server\">the instructions<\/a> to <em>provision<\/em> a Google Tag Manager server-side environment, the scripts automatically create a <strong>single Standard App Engine instance<\/strong>.<\/p>\n<blockquote>\n<p><strong>NOTE!<\/strong> In early beta, there was also the option to load the Server container in a Kubernetes cluster of Compute Engine instances. This is a more advanced setup designed for users who already have a pipeline running in Google Cloud, and they want more control over it than what App Engine\u2019s managed environments can offer.<\/p>\n<\/blockquote>\n<p>App Engine is a managed virtual machine platform running in the Google Cloud. By using the Standard environment and a single instance, you can test-drive your Google Tag Manager setup most likely without even expending the <a href=\"https:\/\/cloud.google.com\/appengine\/quotas\">free quota<\/a> you have available.<\/p>\n<div style=\"aspect-ratio: 3076 \/ 652;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/app-engine-three-instances.jpg\" title=\"App Engine with three instances\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"652\" width=\"3076\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/app-engine-three-instances.jpg#ZgotmplZ\" alt=\"App Engine with three instances\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>However, as soon as you\u2019re ready to deploy the new Server container into a full production environment, you should guarantee best performance and uptime by transferring to a Flexible App Engine environment and increasing the number of instances to a minimum of three. By doing this, you increase the throughput and performance of your server-side endpoint, and you guarantee that it is able to shoulder the incoming load. Follow <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/script-user-guide#create_a_gcp_server\">these instructions<\/a> for more details.<\/p>\n<div style=\"aspect-ratio: 1774 \/ 812;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/custom-domain-mapped.jpg\" title=\"Custom Domain mapped\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"812\" width=\"1774\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/custom-domain-mapped.jpg#ZgotmplZ\" alt=\"Custom Domain mapped\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In addition to provisioning extra instances, you should also <a href=\"#custom-domain\">map a custom domain<\/a> to the endpoint, preferably one that is a subdomain of your main site.<\/p>\n<h3 id=\"cost-1\">Cost<\/h3>\n<p>It\u2019s difficult to say what the <em>exact<\/em> cost for your setup will be, but rest assured that there <em>will<\/em> be costs associated with production usage.<\/p>\n<p>As an example, I\u2019m running App Engine in a Flexible environment, using three instances (the minimum recommended setup for production use). The cost associated with this setup is around <strong>4 euros per day<\/strong>.<\/p>\n<div style=\"aspect-ratio: 3598 \/ 1994;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/app-engine-cost.jpg\" title=\"App Engine cost\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1994\" width=\"3598\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/app-engine-cost.jpg#ZgotmplZ\" alt=\"App Engine cost\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>I\u2019m only collecting Universal Analytics Page Views to this Server container. As you can see, the graph is pretty much steady regardless of the amount of hits coming in (my site averages just <strong>0.2 requests per second<\/strong>).<\/p>\n<p>My site has a visible dip in pageviews over weekends, with around 8,000 pageviews sent over a typical weekday and just one fourth of that over a Saturday or a Sunday. However, these dips don\u2019t reflect in the cost of running my current Server container, which means I could probably scale the setup down a little, but on the other hand I fully intend to add additional measurements, so I\u2019d have to scale back up anyway.<\/p>\n<p>When you compare the cost forecast above with a Server-side tagging setup that collects around <strong>60 requests per second<\/strong>, we\u2019re talking at around <strong>250\u20ac per month<\/strong> instead.<\/p>\n<div style=\"aspect-ratio: 3582 \/ 838;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/08\/forecasted-large-cost.jpg\" title=\"Forecasted cost with a larger throughput\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"838\" width=\"3582\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/08\/forecasted-large-cost.jpg#ZgotmplZ\" alt=\"Forecasted cost with a larger throughput\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Forecasted cost with a larger throughput<\/span><\/p>\n<\/div>\n<p>I hope at some point Google releases case studies and statistics, or even a tool, which allow you to estimate the cost and scale up or down accordingly.<\/p>\n<h3 id=\"server-container\">Server container<\/h3>\n<p>The <strong>Server container<\/strong> itself is visually reminiscent of any Google Tag Manager container.<\/p>\n<div style=\"aspect-ratio: 2548 \/ 1814;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/server-container.jpg\" title=\"Server container\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1814\" width=\"2548\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/server-container.jpg#ZgotmplZ\" alt=\"Server container\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The main difference is the new <strong>Client<\/strong> asset type you can see in the left-hand menu. I\u2019ll explain more about clients in the <a href=\"#clients-and-tags\">associated chapter<\/a>.<\/p>\n<blockquote>\n<p>When using the term <strong>incoming HTTP request<\/strong>, I\u2019m referring to the HTTP request that is sent from a device or browser to the Server container. When using the term <strong>outgoing HTTP request<\/strong>, I\u2019m referring to the HTTP request built and dispatched by tags firing in the container.<\/p>\n<\/blockquote>\n<h4 id=\"tags\">Tags<\/h4>\n<p>Tag-wise there\u2019s not much there, yet.<\/p>\n<div style=\"aspect-ratio: 1546 \/ 924;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/tags.jpg\" title=\"Available tag templates\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"924\" width=\"1546\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/tags.jpg#ZgotmplZ\" alt=\"Available tag templates\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>There\u2019s the native <strong>Universal Analytics<\/strong> and <strong>App + Web<\/strong> templates, both configured to digest data pushed by their respective Clients. The <strong>HTTP Request<\/strong> tag lets you create an outgoing HTTP request to any destination.<\/p>\n<p>Then there are all the <a href=\"#custom-templates\">custom tag templates<\/a> people can imagine creating. Almost any service that accepts HTTP requests can be configured into a custom tag template in the Server container.<\/p>\n<h4 id=\"triggers\">Triggers<\/h4>\n<p>There\u2019s a noticeable lack of available triggers. In fact, there\u2019s just a single <strong>Custom<\/strong> trigger type.<\/p>\n<div style=\"aspect-ratio: 1852 \/ 728;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/custom-trigger.jpg\" title=\"Custom trigger\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"728\" width=\"1852\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/custom-trigger.jpg#ZgotmplZ\" alt=\"Custom trigger\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The fact is that a Server container would not be associated with arbitrary triggers such as \u201cPage View\u201d, \u201cClick\u201d, or \u201cVideo\u201d. Instead, any tags triggering in a Server container would only trigger if a Client <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#runcontainer\">instructed them to do so<\/a>.<\/p>\n<p>A Server container is also unrelated to client-side labels such as a \u201cpage load\u201d or a \u201ccontainer load\u201d. It\u2019s running all the time &#8211; it\u2019s not reset when the page is refreshed. Thus there are no triggers related to the lifecycle of a Server container, though that doesn\u2019t mean there won\u2019t be at some point.<\/p>\n<h4 id=\"variables\">Variables<\/h4>\n<p>The available <strong>Built-in variables<\/strong> are:<\/p>\n<table>\n<thead>\n<tr>\n<th>Variable name<\/th>\n<th>Description<\/th>\n<th>Example<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Query String<\/td>\n<td>Returns the query string of the incoming HTTP request.<\/td>\n<td><code>v=1&amp;t=pageview&amp;tid=UA-12345-1...<\/code><\/td>\n<\/tr>\n<tr>\n<td>Request Method<\/td>\n<td>Returns the method of the incoming HTTP request.<\/td>\n<td><code>GET<\/code><\/td>\n<\/tr>\n<tr>\n<td>Request Path<\/td>\n<td>Returns the path of the incoming HTTP request.<\/td>\n<td><code>\/collect<\/code><\/td>\n<\/tr>\n<tr>\n<td>Client Name<\/td>\n<td>Returns the name of the <a href=\"#clients-and-tags\">Client<\/a> currently processing the request.<\/td>\n<td><code>Facebook Client<\/code><\/td>\n<\/tr>\n<tr>\n<td>Container ID<\/td>\n<td>Returns the ID of your Server container.<\/td>\n<td><code>GTM-XXXXXX<\/code><\/td>\n<\/tr>\n<tr>\n<td>Container Version<\/td>\n<td>Returns the current version of your Server container.<\/td>\n<td><code>QUICK_PREVIEW<\/code><\/td>\n<\/tr>\n<tr>\n<td>Debug Mode<\/td>\n<td>Whether the container is in Preview mode or not.<\/td>\n<td><code>true<\/code><\/td>\n<\/tr>\n<tr>\n<td>Random Number<\/td>\n<td>Returns a random positive integer.<\/td>\n<td><code>12345<\/code><\/td>\n<\/tr>\n<tr>\n<td>Event Name<\/td>\n<td>Returns the value of the <code>event_name<\/code> field in the <a href=\"#event-model\">event data object<\/a> that was passed to the container.<\/td>\n<td><code>page_view<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>These can be used to parse information about the <a href=\"#requests-and-responses\">incoming request<\/a> and to retrieve metadata about the event that fired and the container itself.<\/p>\n<p>Available <strong>User-defined variables<\/strong> are:<\/p>\n<table>\n<thead>\n<tr>\n<th>Variable name<\/th>\n<th>Description<\/th>\n<th>Example<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Cookie Value<\/td>\n<td>Set to the value of the first cookie that matches the name.<\/td>\n<td><code>GA1.2.12345.12345<\/code><\/td>\n<\/tr>\n<tr>\n<td>Query Parameter<\/td>\n<td>Set to the value of the first query parameter in the <strong>incoming HTTP request<\/strong> that matches the name.<\/td>\n<td><code>UA-12345-1<\/code><\/td>\n<\/tr>\n<tr>\n<td>Query String<\/td>\n<td>Returns the query string of the incoming HTTP request. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>v=1&amp;tid=UA-12345-1&amp;t=pageview...<\/code><\/td>\n<\/tr>\n<tr>\n<td>Request Header<\/td>\n<td>Returns the value(s) of the header name from the incoming HTTP request.<\/td>\n<td><code>https:\/\/referrer-page.com\/<\/code><\/td>\n<\/tr>\n<tr>\n<td>Request Method<\/td>\n<td>Returns the method of the incoming HTTP request. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>POST<\/code><\/td>\n<\/tr>\n<tr>\n<td>Request Path<\/td>\n<td>Returns the path of the incoming HTTP request. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>\/j\/collect<\/code><\/td>\n<\/tr>\n<tr>\n<td>Client Name<\/td>\n<td>Returns the name of the Client currently processing the request. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>Universal Analytics<\/code><\/td>\n<\/tr>\n<tr>\n<td>Constant<\/td>\n<td>Returns whatever string you type into the variable.<\/td>\n<td><code>UA-12345-1<\/code><\/td>\n<\/tr>\n<tr>\n<td>Event Data<\/td>\n<td>Returns the value of the key in the <a href=\"#event-model\">event data object<\/a>.<\/td>\n<td><code>123.123.123.123<\/code><\/td>\n<\/tr>\n<tr>\n<td>Event Name<\/td>\n<td>Returns the value of the <code>event_name<\/code> field in the event data object that was passed to the container. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>page_view<\/code><\/td>\n<\/tr>\n<tr>\n<td>Random Number<\/td>\n<td>Returns a random positive integer. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>123123<\/code><\/td>\n<\/tr>\n<tr>\n<td>Container ID<\/td>\n<td>Returns the ID of your Server container. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>GTM-ABCDE<\/code><\/td>\n<\/tr>\n<tr>\n<td>Container Version Number<\/td>\n<td>Returns the current version of your Server container. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>13<\/code><\/td>\n<\/tr>\n<tr>\n<td>Debug Mode<\/td>\n<td>Whether the container is in Preview mode or not. <strong>Note! Use the Built-in variable instead.<\/strong><\/td>\n<td><code>false<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You can, and should, utilize <a href=\"#custom-templates\">custom templates<\/a> to build your own variable types.<\/p>\n<h3 id=\"custom-domain\">Custom domain<\/h3>\n<p>You are <strong>strongly<\/strong> encouraged to map a <em>custom domain<\/em> to your Server container endpoint.<\/p>\n<p>The main reason is that this way you can incorporate the server-side data collection endpoint <strong>that you own<\/strong> in your first-party domain namespace. For example, I\u2019m using <code>sgtm.simoahava.com<\/code> as the host of the Server container.<\/p>\n<p>This becomes significant when you consider things like <a href=\"https:\/\/www.cookiestatus.com\/safari\/\">Intelligent Tracking Prevention<\/a>. You might want to make use of cookies in the incoming requests, but if the Server container is hosted on a domain that is different from where the requests are sent (such as your site), these cookies will be considered <strong>third-party cookies<\/strong> and thus dropped by many browsers.<\/p>\n<blockquote>\n<p><strong>NOTE!<\/strong> Due to <a href=\"https:\/\/bugs.webkit.org\/show_bug.cgi?id=215201\">upcoming changes<\/a> in ITP, you should map the domain as a newly verified subdomain. This way you\u2019ll be instructed to use A\/AAAA DNS records rather than the vulnerable CNAME alias.<\/p>\n<\/blockquote>\n<div style=\"aspect-ratio: 1762 \/ 802;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/appengine-domains.jpg\" title=\"App Engine domains\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"802\" width=\"1762\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/appengine-domains.jpg#ZgotmplZ\" alt=\"App Engine domains\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Similarly, having the endpoint in your first-party domain namespace means you can do things like <a href=\"#server-side-universal-analytics-with-a-set-cookie-header\">set first-party cookies with Set-Cookie headers<\/a> and thus avoid Safari expiring them within 7 days of being set.<\/p>\n<blockquote>\n<p>Note that technically you can have <strong>more than one domain pointing to your App Engine deployment<\/strong>. This is helpful in case you have a single server-side container responding to requests from multiple domains. The only \u201ccatch\u201d is that some features of the Server container, such as for which domain the <a href=\"#preview-and-debug\">Preview mode<\/a> is shown, are restricted to just one domain. It won\u2019t hamper the actual data collection, but it might make it difficult to use some of these features.<\/p>\n<\/blockquote>\n<h3 id=\"requests-and-responses\">Requests and responses<\/h3>\n<p>Server-side tagging revolves around <strong>incoming HTTP requests<\/strong> to the server container being mapped by a <a href=\"#clients-and-tags\">Client<\/a>, passed to a tag, and then dispatched as an <strong>outgoing HTTP request<\/strong> to the tag vendor. Once all the tags have fired for a given Client, the Client sends an HTTP response back to the origin of the request, such as a browser, an app, or some other connected service.<\/p>\n<p>This flow is <em>absolutely fundamental<\/em> to understanding how Server-side tagging works. A \u201cperfect\u201d end-to-end pipeline would be one where the requests are carefully sculpted to make use of as little client-side code as possible; The Clients are designed to handle both vendor-specific and vendor-agnostic requests; The tags are built to trigger off specific clients\u2019 event data payloads, finally responding to the Client whether the outgoing HTTP request was a success or not.<\/p>\n<p>The <em>easiest<\/em> way to map a Client to an incoming request is to observe the <strong>Request Path<\/strong>. The built-in Universal Analytics Client, for example, is primed to listen to requests that have the <code>\/collect<\/code> path or any of its permutations (such as <code>\/j\/collect<\/code> or <code>\/r\/collect<\/code>). You could create a Facebook Client that listens for a custom path of <code>\/fbook<\/code>, and a HotJar Client that listens for requests with the path of <code>\/hotjar<\/code>.<\/p>\n<div style=\"aspect-ratio: 1136 \/ 198;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/facebook-client.jpg\" title=\"Client looks for a request to \/fbq and if one is found, claims the request.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"198\" width=\"1136\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/facebook-client.jpg#ZgotmplZ\" alt=\"Client looks for a request to \/fbq and if one is found, claims the request.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Client looks for a request to \/fbq and if one is found, claims the request.<\/span><\/p>\n<\/div>\n<p>Alternatively, you could approach a <em>single event stream<\/em> model, where all requests are sent to the same Client. In this case, they would have just a single path, such as <code>\/events<\/code>, and you would configure the Client to parse the request body and turn it into event data objects that are passed to the container.<\/p>\n<p>Whatever you choose, you need to remember that whatever origin sent the request is actually waiting for a <strong>response<\/strong> back from the Server container.<\/p>\n<p>By default, the response is a fairly nondescript <code>text\/html<\/code> response, but you can jazz things up using the following APIs.<\/p>\n<ul>\n<li><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#setcookie\"><code>setCookie<\/code><\/a> lets you write cookies in the Set-Cookie header of the HTTP response.<\/li>\n<li><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#setpixelresponse\"><code>setPixelResponse<\/code><\/a> automatically configures the response to mimic a 1&#215;1 GIF pixel with headers that prevent caching.<\/li>\n<li><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#setresponseheader\"><code>setResponseHeader<\/code><\/a> can be used to add <em>any<\/em> custom HTTP headers to the response. Consider headers like <code>Access-Control-Allow-Origin<\/code> and <code>Access-Control-Allow-Credentials<\/code>, which are useful for preflight requests, for example.<\/li>\n<\/ul>\n<div style=\"aspect-ratio: 1890 \/ 740;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/request-with-headers.jpg\" title=\"Request with setPixelResponse() and setCookie() APIs.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"740\" width=\"1890\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/request-with-headers.jpg#ZgotmplZ\" alt=\"Request with setPixelResponse() and setCookie() APIs.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Request with setPixelResponse() and setCookie() APIs.<\/span><\/p>\n<\/div>\n<p>Hopefully at some point we\u2019ll see more options for manipulating the <strong>request<\/strong> as well. We could envision Clients whose only purpose is to purge the incoming request from PII, before passing the request on to the next client that actually builds the event object for tags to dispatch, now cleared of all PII.<\/p>\n<h3 id=\"clients-and-tags\">Clients and tags<\/h3>\n<p>We\u2019ve already talked a lot about <strong>Clients<\/strong> and <strong>tags<\/strong>, but it\u2019s good to reiterate as the concept might be a bit alien &#8211; especially if you\u2019re used to how GTM for the web works.<\/p>\n<p>The purpose of a <strong>Client<\/strong> is to listen for <strong>incoming HTTP requests<\/strong>, parse them into a unified <a href=\"#event-model\">event model<\/a>, and then run a virtual container with the event model, so that tags can use the details in these event objects to compile the requests to their endpoints.<\/p>\n<p>Because Clients do all the legwork, you can start streamlining the event stream itself, moving away from vendor-specific request parsing (which might require vendor-specific JavaScript to run in the user\u2019s browser, for example), and leaning towards a more agnostic approach, where a single event stream can be distributed into multiple unrelated endpoints.<\/p>\n<p>Clients operate on a <strong>Priority<\/strong> order. The higher the priority of a Client, the sooner it gets to check if the request is its to <strong>claim<\/strong>. To <strong>claim<\/strong> a request means calling the <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#claimrequest\"><code>claimRequest<\/code> API<\/a>. This, in turn, means that the current Client tells all the other Clients that this request is <strong>MINE!<\/strong> and doesn\u2019t allow any other Client to parse the request anymore.<\/p>\n<div style=\"aspect-ratio: 1960 \/ 686;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/client-priority.jpg\" title=\"The Client with Priority 100 always has first dibs on the request.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"686\" width=\"1960\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/client-priority.jpg#ZgotmplZ\" alt=\"The Client with Priority 100 always has first dibs on the request.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">The Client with Priority 100 always has first dibs on the request.<\/span><\/p>\n<\/div>\n<p>Once the Client has parsed the request and built an event object out of it, the event is passed to the <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#runcontainer\"><code>runContainer<\/code> API<\/a>. With this API, the event is passed to the tags to evaluate and potentially trigger with.<\/p>\n<p>Tags can be set to trigger on numerous different things, but most likely you will end up using a combination of <strong>Event Name<\/strong> and\/or <strong>Client Name<\/strong>.<\/p>\n<p>The Event Name is something that <a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/gtagjs\/events\">gtag.js<\/a>, <a href=\"https:\/\/firebase.google.com\/docs\/analytics\">Firebase Analytics<\/a>, and more recently <a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/app-web\/events\">App + Web<\/a> iterated and introduced to the world of web analytics.<\/p>\n<p>Basically, there\u2019s an inventory of <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/events\"><strong>suggested<\/strong> or quasi-<strong>standardized<\/strong> Event Names<\/a>, such as <code>page_view<\/code>, <code>search<\/code>, and <code>login<\/code>. Then there is always the opportunity to use a custom Event Name such as <code>new_level_achieved<\/code>.<\/p>\n<p>When the Client builds the event model, it <em>has<\/em> to provide an Event Name. This is what ends up showing up in the Preview screen when the request is claimed and mapped by a Client:<\/p>\n<div style=\"aspect-ratio: 2016 \/ 1054;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/event-name.jpg\" title=\"Event name\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1054\" width=\"2016\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/event-name.jpg#ZgotmplZ\" alt=\"Event name\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>So if you wanted to fire a bunch of tags whenever a <code>page_view<\/code> is collected, regardless of Client, you\u2019d simply use a trigger that checks the Event Name. You\u2019d then just need to <em>assume<\/em> that the Client has correctly intercepted the incoming HTTP request, and has managed to parse and map it into an event object that can be understood by your tag (you can use Preview mode to analyze what the <strong>event data object<\/strong> contained).<\/p>\n<div style=\"aspect-ratio: 1776 \/ 552;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/event-page-view.jpg\" title=\"Event name page view\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"552\" width=\"1776\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/event-page-view.jpg#ZgotmplZ\" alt=\"Event name page view\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Alternatively, perhaps you want your tag to fire only when the Facebook Client generated the event object. This is useful in case the tag requires a <em>lot<\/em> of custom information not available as standard parameters in the event object.<\/p>\n<p>By referencing the Client Name, you can ensure that the tag only fires if the correct Client has claimed the incoming HTTP request, assuming the Client that executed <code>runContainer<\/code> also <em>claimed<\/em> the request (there might be some edge cases where this is not the case).<\/p>\n<div style=\"aspect-ratio: 1600 \/ 554;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/facebook-client-trigger.jpg\" title=\"Facebook Client trigger\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"554\" width=\"1600\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/facebook-client-trigger.jpg#ZgotmplZ\" alt=\"Facebook Client trigger\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>It might be difficult to wrap your head around Clients and tags, but once the inventory of templates for both multiplies in the <a href=\"https:\/\/tagmanager.google.com\/gallery\">community gallery<\/a>, it will become easier to just use the community templates rather than having to worry about how to build your own.<\/p>\n<h3 id=\"event-model\">Event model<\/h3>\n<p>When the Client parses an incoming HTTP request it has claimed, it needs to map values in the request body (typically in a query string) and produce an <strong>event data object<\/strong>, which looks something like this:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\">{\n  event_name: <span style=\"color:#a50\">\"page_view\"<\/span>,\n  event_parameter: <span style=\"color:#a50\">\"some value\"<\/span>,\n  event_parameter_object: {\n    nested_event_parameter: <span style=\"color:#a50\">\"some other value\"<\/span>\n  }\n}\n<\/code><\/pre>\n<\/div>\n<p>This object is what gets passed to the container with the <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#runcontainer\"><code>runContainer<\/code> API<\/a>. Tags will then be able to use these values in the trigger, e.g. firing a tag only when <code>event_name<\/code> is <code>page_view<\/code>, and they\u2019ll be able to map values in the event object to the outgoing HTTP request they dispatch to the vendor endpoint.<\/p>\n<p>To keep things streamlined, Google <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/events\">suggests a set of standard event names and parameters<\/a> that you should try to follow to make sure that the event data object passed to the container can be used with as little friction as possible. If the tags require parameters that are not available in the list of standard parameters, they should be prefixed with <code>x-vendor-<\/code>. Thus, for example, Facebook\u2019s <a href=\"https:\/\/developers.facebook.com\/docs\/marketing-api\/conversions-api\/parameters\/customer-information-parameters#subscription-id\">Subscription ID<\/a> becomes <code>x-fb-subscription_id<\/code>.<\/p>\n<p>Note also the preference of <code>snake_case<\/code> vs. <code>camelCase<\/code>. It\u2019s a syntactical format you should get accustomed to using when working with Server-side tagging.<\/p>\n<p>You can always use the Server container\u2019s <a href=\"#preview-and-debug\">Preview mode<\/a> to audit what\u2019s passed in the event data object by any given client. For example, when collecting a Universal Analytics Measurement Protocol hit, this is what you might see:<\/p>\n<div style=\"aspect-ratio: 1740 \/ 2398;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/ga-event-data-object.jpg\" title=\"Event data object\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"2398\" width=\"1740\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/ga-event-data-object.jpg#ZgotmplZ\" alt=\"Event data object\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In the example above, many <em>standard parameters<\/em> are populated, such as <code>client_id<\/code>, <code>ip_override<\/code>, <code>page_location<\/code>, and <code>user_agent<\/code>.<\/p>\n<p>Additionally, Measurement Protocol uses a number of custom parameters that are (more or less) unique to Google Analytics, such as <code>x-ga-mp1-vp<\/code> (<a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/protocol\/v1\/parameters#vp\">viewport size<\/a>), <code>x-ga-measurement_id<\/code> (<a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/protocol\/v1\/parameters#tid\">web property ID<\/a> with Universal Analytics), and <code>x-ga-mp1-plt<\/code> (<a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/protocol\/v1\/parameters#plt\">page load time<\/a>).<\/p>\n<p>This event object is then digested by the Universal Analytics tag, which will be able to take these items and compile the outgoing Measurement Protocol request to Google Analytics. If the event object is correctly compiled, the tag can even utilize the shorthand API <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#sendeventtogoogleanalytics\"><code>sendEventToGoogleAnalytics<\/code><\/a>.<\/p>\n<h3 id=\"custom-templates\">Custom templates<\/h3>\n<p>Server-side tagging relies heavily on <a href=\"https:\/\/www.simoahava.com\/analytics\/custom-templates-guide-for-google-tag-manager\/\">custom templates<\/a>. In addition to <strong>tag<\/strong> and <strong>variable<\/strong> templates, which are available in web containers as well, power users now have the opportunity to create <strong>Client<\/strong> templates as well.<\/p>\n<p>The available APIs for these are listed <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api\">in the documentation<\/a>. Many APIs, such as <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#runcontainer\"><code>runContainer<\/code><\/a> have a footnote saying:<\/p>\n<blockquote>\n<p>It is recommended that this API be used from a client template.<\/p>\n<\/blockquote>\n<p>Roughly, Clients should typically utilize APIs that <strong>claim, validate, and parse<\/strong> the incoming HTTP requests, <strong>run the container<\/strong> with the event data object, <strong>listen for messages<\/strong> from the tags fired in the container, and finally <strong>modify and return a response<\/strong> back to the source of the incoming request.<\/p>\n<p>Here are some APIs that you\u2019d <em>typically<\/em> run exclusively from a Client:<\/p>\n<table>\n<thead>\n<tr>\n<th>Client API<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#claimRequest\"><code>claimRequest<\/code><\/a><\/td>\n<td>Claim the request for the Client.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#extracteventsfrommpv1\"><code>extractEventsFromMpv1<\/code><\/a><\/td>\n<td>Parse an incoming Measurement Protocol v1 request, and extract event data objects from it.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#extracteventsfrommpv2\"><code>extractEventsFromMpv2<\/code><\/a><\/td>\n<td>Parse an incoming Measurement Protocol v2 request, and extract event data objects from it.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#getcookievalues\"><code>getCookieValues<\/code><\/a><\/td>\n<td>Get the values of all cookies with the given name in the incoming HTTP request.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#getremoteaddress\"><code>getRemoteAddress<\/code><\/a><\/td>\n<td>Best-effort attempt to get the IP address of the incoming request.<\/td>\n<\/tr>\n<tr>\n<td><code>getRequest*<\/code><\/td>\n<td>All the <code>getRequest...<\/code> APIs are designed to parse some aspect of the incoming HTTP request.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#isrequestmpv1\"><code>isRequestMpv1<\/code><\/a><\/td>\n<td>Check if the incoming HTTP request is in Measurement Protocol v1 format.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#isrequestmpv2\"><code>isRequestMpv2<\/code><\/a><\/td>\n<td>Check if the incoming HTTP request is in Measurement Protocol v2 format.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#returnresponse\"><code>returnResponse<\/code><\/a><\/td>\n<td>Return the HTTP response with all the set headers back to the source of the incoming request.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#runcontainer\"><code>runContainer<\/code><\/a><\/td>\n<td>Run the container with the event data object.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#setcookie\"><code>setCookie<\/code><\/a><\/td>\n<td>Populate a Set-Cookie header in the response.<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#setPixelResponse\"><code>setPixelResponse<\/code><\/a><\/td>\n<td>Automatically set response headers to mimic a 1&#215;1 GIF pixel.<\/td>\n<\/tr>\n<tr>\n<td><code>setResponse*<\/code><\/td>\n<td>All the <code>setResponse...<\/code> headers modify some aspect of the HTTP response finally flushed by the <code>returnResponse<\/code> API.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<blockquote>\n<p>Note that you certainly <em>can<\/em> use some of these APIs in tags. For example, you could set aspects of the response directly in the tag itself. However, it might make sense to have tags communicate their status back to the Client with the <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#sendmessage\"><code>sendMessage<\/code> API<\/a>, and use the Client to manage all aspects of the request-response flow.<\/p>\n<\/blockquote>\n<p>Tags should typically utilize APIs that <strong>parse the event data object<\/strong>, build an <strong>HTTP request<\/strong> to the tag endpoint, and <strong>message back to the container<\/strong> whatever metadata the outgoing request produced (such as a failed status code or success message).<\/p>\n<p>Here are some APIs that you\u2019d <em>typically<\/em> run exclusively from a Tag:<\/p>\n<blockquote>\n<p>Note that you <em>could<\/em> run a Server container without a single tag. All the APIs designed to be used in tags could be run through a Client. But this is orthogonal to how Server-side tagging has been designed to work.<\/p>\n<\/blockquote>\n<p>Here\u2019s an example of a Client running some of the recommended APIs:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#00a\">const<\/span> addMessageListener = require(<span style=\"color:#a50\">'addMessageListener'<\/span>);\n<span style=\"color:#00a\">const<\/span> claimRequest = require(<span style=\"color:#a50\">'claimRequest'<\/span>);\n<span style=\"color:#00a\">const<\/span> extractEventsFromMpv1 = require(<span style=\"color:#a50\">'extractEventsFromMpv1'<\/span>);\n<span style=\"color:#00a\">const<\/span> isRequestMpv1 = require(<span style=\"color:#a50\">'isRequestMpv1'<\/span>);\n<span style=\"color:#00a\">const<\/span> returnResponse = require(<span style=\"color:#a50\">'returnResponse'<\/span>);\n<span style=\"color:#00a\">const<\/span> runContainer = require(<span style=\"color:#a50\">'runContainer'<\/span>);\n<span style=\"color:#00a\">const<\/span> setResponseBody = require(<span style=\"color:#a50\">'setResponseBody'<\/span>);\n<span style=\"color:#00a\">const<\/span> setResponseHeader = require(<span style=\"color:#a50\">'setResponseHeader'<\/span>);\n<span style=\"color:#00a\">const<\/span> setResponseStatus = require(<span style=\"color:#a50\">'setResponseStatus'<\/span>);\n<span style=\"color:#00a\">const<\/span> setPixelResponse = require(<span style=\"color:#a50\">'setPixelResponse'<\/span>);\n\n<span style=\"color:#aaa;font-style:italic\">\/\/ If Measurement Protocol request, claim and parse\n<\/span><span style=\"color:#aaa;font-style:italic\"\/><span style=\"color:#00a\">if<\/span> (isRequestMpv1()) {\n  claimRequest();\n  <span style=\"color:#00a\">const<\/span> events = extractEventsFromMpv1();\n  \n  <span style=\"color:#aaa;font-style:italic\">\/\/ Listen for message from tag signalling completion,\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#aaa;font-style:italic\">\/\/ set response headers accordingly.\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  addMessageListener(<span style=\"color:#a50\">'ga_complete'<\/span>, (messageType, message) =&gt; {\n    <span style=\"color:#00a\">if<\/span> (message.status === <span style=\"color:#a50\">'error'<\/span>) {\n      setResponseStatus(<span style=\"color:#099\">500<\/span>);\n      setResponseBody(message.body);\n    } <span style=\"color:#00a\">else<\/span> <span style=\"color:#00a\">if<\/span> (message.status === <span style=\"color:#a50\">'redirect'<\/span>) {\n      setResponseStatus(<span style=\"color:#099\">302<\/span>);\n      setResponseHeader(<span style=\"color:#a50\">'location'<\/span>, message.location);\n    }\u00a0<span style=\"color:#00a\">else<\/span> {\n      setPixelResponse();\n    }\n  });\n  \n  <span style=\"color:#aaa;font-style:italic\">\/\/ Run the container with the parsed event object\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#00a\">let<\/span> eventsCompleted = <span style=\"color:#099\">0<\/span>;\n  events.forEach(event =&gt; {\n    runContainer(event, () =&gt; {\n      <span style=\"color:#aaa;font-style:italic\">\/\/ If all the events in the incoming HTTP request have been completed\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>      <span style=\"color:#00a\">if<\/span> (events.length === ++eventsCompleted) {\n        returnResponse();\n      }\n    });\n  });\n}\n<\/code><\/pre>\n<\/div>\n<p>And here\u2019s what the corresponding tag might do:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#00a\">const<\/span> getAllEventData = require(<span style=\"color:#a50\">'getAllEventData'<\/span>);\n<span style=\"color:#00a\">const<\/span> sendEventToGoogleAnalytics = require(<span style=\"color:#a50\">'sendEventToGoogleAnalytics'<\/span>);\n<span style=\"color:#00a\">const<\/span> sendMessage = require(<span style=\"color:#a50\">'sendMessage'<\/span>);\n\n<span style=\"color:#aaa;font-style:italic\">\/\/ Access the event data object\n<\/span><span style=\"color:#aaa;font-style:italic\"\/><span style=\"color:#00a\">const<\/span> event = getAllEventData();\n\n<span style=\"color:#aaa;font-style:italic\">\/\/ Send the event to Google Analytics and parse the response\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>sendEventToGoogleAnalytics(event, response =&gt; {\n  <span style=\"color:#00a\">if<\/span> (!response.success) {\n    sendMessage(<span style=\"color:#a50\">'ga_complete'<\/span>, {\n      status: <span style=\"color:#a50\">'error'<\/span>,\n      body: <span style=\"color:#a50\">'Request to Google Analytics failed'<\/span>\n    });\n    <span style=\"color:#00a\">return<\/span> data.gtmOnFailure();\n  }\n  <span style=\"color:#00a\">if<\/span> (response.location) {\n    sendMessage(<span style=\"color:#a50\">'ga_complete'<\/span>, {\n      status: <span style=\"color:#a50\">'redirect'<\/span>,\n      location: response.location\n    });\n  } <span style=\"color:#00a\">else<\/span> {\n    sendMessage(<span style=\"color:#a50\">'ga_complete'<\/span>, {\n      status: <span style=\"color:#a50\">'success'<\/span>\n    });\n  }\n  data.gtmOnSuccess();\n});\n<\/code><\/pre>\n<\/div>\n<p>As you can see, the Client parses the incoming request and sends it as an event data object to the container. The container then triggers the tag(s), which map the event data object to a Measurement Protocol request.<\/p>\n<p>Google Analytics is a bit special in this case, because both the incoming request parser (<code>extractEventsFromMpv1<\/code>) and the outgoing request dispatcher (<code>sendEventToGoogleAnalytics<\/code>) have their own, dedicated APIs built. If you use a custom vendor endpoint, you need to actually manually write the code that turns an incoming request query string into the event data object, and which takes the event data object and maps it to an outgoing HTTP request.<\/p>\n<p>The <code>addMessageListener<\/code> and <code>sendMessage<\/code> APIs are very useful, as they allow tags and Clients to communicate with each other. This is very helpful, in case you want the Client to encode information about tag execution in the response back to the request source.<\/p>\n<blockquote>\n<p><strong>Note!<\/strong> The sample code above is a bit unwieldy, because it assumes <code>runContainer<\/code> to only trigger one tag. If there\u2019s more than one tag firing, the <code>addMessageListener<\/code> callback would react to each tag messaging back, which means only the message from the <em>last<\/em> tag that fired would be considered for the response the Client sends back to the source of the incoming request.<\/p>\n<\/blockquote>\n<h3 id=\"preview-and-debug\">Preview and debug<\/h3>\n<p>The Server container, just like a web container, has its own Preview and Debug mode. When you click the <strong>Preview<\/strong> button in the user interface, a <strong>new tab<\/strong> opens with the Preview interface.<\/p>\n<p>Just like with a web container, the Preview tab only shows hits that originate from <em>your<\/em> browser &#8211; and it has to be the same browser instance that started Preview mode.<\/p>\n<blockquote>\n<p>If you want to manually force hits to appear in Preview mode (for example when dispatching them server-to-server or when testing with <code>curl<\/code>), you need to <strong>add a custom header to those requests<\/strong>. You can find instructions on how to add the custom header <a href=\"https:\/\/www.simoahava.com\/gtmtips\/preview-server-side-google-tag-manager\/\">in this article<\/a>.<\/p>\n<\/blockquote>\n<div style=\"aspect-ratio: 2306 \/ 886;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/preview-panel.jpg\" title=\"Preview panel\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"886\" width=\"2306\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/preview-panel.jpg#ZgotmplZ\" alt=\"Preview panel\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In the left-hand side navigation, you see all the <strong>incoming HTTP requests<\/strong> and whether or not a Client has claimed and created an event data object from the request. In the screenshot, no event data object was created for the <code>favicon.ico<\/code> request, but both <code>collect<\/code> requests were claimed by a Client and turned into event data objects.<\/p>\n<p>If you select a <strong>request<\/strong>, all the tabs in Preview mode will behave as if you\u2019d selected <strong>Summary<\/strong>. In other words, you\u2019ll be able to see <em>how many times<\/em> a tag would have fired for the request, but you wouldn\u2019t be able to look into <strong>Variables<\/strong> or <strong>Event Data<\/strong>.<\/p>\n<p>For this reason, whenever you debug, you should choose the <strong>event data object<\/strong> (e.g. <code>page_view<\/code> in the example) if available.<\/p>\n<h4 id=\"request-tab\">Request tab<\/h4>\n<p>The <strong>Request<\/strong> tab contains information about the incoming HTTP request and the response sent back by the Server container. It will also show you if a Client <em>claimed<\/em> the request.<\/p>\n<div style=\"aspect-ratio: 1960 \/ 886;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/08\/preview-request.jpg\" title=\"Preview and Request tab\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"886\" width=\"1960\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/08\/preview-request.jpg#ZgotmplZ\" alt=\"Preview and Request tab\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The very first line contains a summary of the HTTP request itself. After that is the value of the <code>event_name<\/code> field, parsed by the Client from the incoming HTTP request.<\/p>\n<p>The <strong>Client<\/strong> box tells you which Client claimed the request.<\/p>\n<p>The <strong>Incoming HTTP Request<\/strong> box opens up a new overlay when clicked, with details about the request.<\/p>\n<div style=\"aspect-ratio: 1880 \/ 2032;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/08\/request-details.jpg\" title=\"Request details\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"2032\" width=\"1880\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/08\/request-details.jpg#ZgotmplZ\" alt=\"Request details\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The <strong>Request<\/strong> overview shows you the Request method (e.g. <code>GET<\/code> or <code>POST<\/code>), and the URL to which the request was sent.<\/p>\n<p><strong>Request Headers<\/strong> list all the HTTP headers present in the request, and the <strong>Request Body<\/strong> card shows what was sent as the body of the request (typically just in <code>POST<\/code> requests).<\/p>\n<div style=\"aspect-ratio: 1834 \/ 704;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/request-response.jpg\" title=\"Response details\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"704\" width=\"1834\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/request-response.jpg#ZgotmplZ\" alt=\"Response details\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The <strong>Response<\/strong> overview has details about what the Server container responded with back to whatever source sent the incoming HTTP request in the first place.<\/p>\n<p><strong>Status Code<\/strong> indicates whether or not the request was a success (<code>200<\/code>).<\/p>\n<p><strong>Response Headers<\/strong> typically include things like cache headers for preventing the browser from caching the request, and <code>Set-Cookie<\/code> headers which write a cookie on the domain running the Server container.<\/p>\n<p>If the response has a body, it\u2019s displayed in the <strong>Response Body<\/strong> card.<\/p>\n<h4 id=\"tags-tab\">Tags tab<\/h4>\n<p>The <strong>Tags<\/strong> tab is pretty self-explanatory. You\u2019ll see all the tags that fired (or did not fire) with this event data object. Similar to a web container, you can click open a tag to see what values it sent, and you can scroll down to its triggers to see why it didn\u2019t fire.<\/p>\n<div style=\"aspect-ratio: 1752 \/ 1402;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/08\/tags-tab.jpg\" title=\"The Tags tab\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1402\" width=\"1752\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/08\/tags-tab.jpg#ZgotmplZ\" alt=\"The Tags tab\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>One cool addition is the <strong>Outgoing HTTP Requests<\/strong> box. When you click it, a new overlay opens with details about the HTTP requests <strong>sent by the tag<\/strong>.<\/p>\n<div style=\"aspect-ratio: 1898 \/ 1844;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/08\/outgoing-http-request.jpg\" title=\"Outgoing HTTP requests\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1844\" width=\"1898\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/08\/outgoing-http-request.jpg#ZgotmplZ\" alt=\"Outgoing HTTP requests\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>You can use this information to debug whether or not the hit to the vendor was dispatched correctly.<\/p>\n<h4 id=\"variables-tab\">Variables tab<\/h4>\n<p>The <strong>Variables<\/strong> tab, similarly, tells you what the value of each configured variable in the container was when the container was executed with the event data object.<\/p>\n<div style=\"aspect-ratio: 1686 \/ 1098;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/variables-tab.jpg\" title=\"Variables tab\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1098\" width=\"1686\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/variables-tab.jpg#ZgotmplZ\" alt=\"Variables tab\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>This is a useful list of information, as you can use it to debug why a tag might not have sent the correct values to the endpoint.<\/p>\n<h4 id=\"event-data-tab\">Event Data tab<\/h4>\n<p>This is a very important tab to familiarize yourself with because it shows you all the values parsed from the incoming HTTP request into an <a href=\"#event-model\">event data object<\/a>. You can use this with the <strong>Request<\/strong> tab to see which parameters might be missing or which were parsed incorrectly.<\/p>\n<div style=\"aspect-ratio: 1848 \/ 1746;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/07\/event-data-preview.jpg\" title=\"Event Data in Preview\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1746\" width=\"1848\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/07\/event-data-preview.jpg#ZgotmplZ\" alt=\"Event Data in Preview\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<h4 id=\"errors-tab\">Errors tab<\/h4>\n<p>If any tag throws an error, this tab would have more information about it. You can read more about the <a href=\"https:\/\/www.simoahava.com\/analytics\/new-errors-tab-preview-mode\/\">errors tab<\/a>.<\/p>\n<h2 id=\"resources\">Resources<\/h2>\n<p>Hopefully, this article will serve as a solid resource for you, especially when you\u2019re getting your feet wet with Server-side tagging.<\/p>\n<p>In addition to this, I\u2019d like to direct you to the official documentation:<\/p>\n<h2 id=\"summary\">Summary<\/h2>\n<p>I fully expect Google Tag Manager\u2019s Server-side tagging to change the landscape of digital analytics. They\u2019re not the first vendor to introduce a handy way of building server-side proxies, but they\u2019re Google, and this service doesn\u2019t come with a license cost.<\/p>\n<p>There are obviously many <a href=\"#key-concerns\">concerns<\/a> about Server-side tagging. Moving tracking behind the veil of the server will most certainly bristle some hairs. For this reason, I recommend you strive for absolute transparency when disclosing details about what tracking is going on either directly or indirectly in your digital assets and properties.<\/p>\n<p>I\u2019m certain we\u2019ll see a proliferation of new custom templates introduced specifically for the Server container, and I also expect most vendors in the adtech and martech space to facilitate server-to-server communication for their pixels and data collection endpoints.<\/p>\n<p>Please let me and other readers know in the comments what you think of Server-side tagging. Do let me know if there are things in this article that require clarification.<\/p>\n<\/p><\/div>\n<p><script async src=\"\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script><br \/>\n<br \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ever since Server-side tagging was publicly announced at SUPERWEEK 2020, Google and the trusted tester community have been hard at work, building something that just might change the landscape of digital analytics for good. Google Tag Manager has now released Server-side tagging into public beta. In this lengthy article, we\u2019ll take a look at what [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":84482,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12033],"tags":[1531,14495,18475,1791,29117],"dealstore":[],"offerexpiration":[],"class_list":["post-84481","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-google","tag-manager","tag-serverside","tag-tag","tag-tagging"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Server-side Tagging In Google Tag Manager - Som2ny Network<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fivemor.com\/?p=84481\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Server-side Tagging In Google Tag Manager - Som2ny Network\" \/>\n<meta property=\"og:description\" content=\"Ever since Server-side tagging was publicly announced at SUPERWEEK 2020, Google and the trusted tester community have been hard at work, building something that just might change the landscape of digital analytics for good. Google Tag Manager has now released Server-side tagging into public beta. In this lengthy article, we\u2019ll take a look at what [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fivemor.com\/?p=84481\" \/>\n<meta property=\"og:site_name\" content=\"Som2ny Network\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-12T19:42:38+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1287\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"45 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fivemor.com\/?p=84481#article\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/?p=84481\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\"},\"headline\":\"Server-side Tagging In Google Tag Manager\",\"datePublished\":\"2025-02-12T19:42:38+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=84481\"},\"wordCount\":8606,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=84481#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg\",\"keywords\":[\"Google\",\"Manager\",\"Serverside\",\"TAG\",\"Tagging\"],\"articleSection\":[\"Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fivemor.com\/?p=84481#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fivemor.com\/?p=84481\",\"url\":\"https:\/\/fivemor.com\/?p=84481\",\"name\":\"Server-side Tagging In Google Tag Manager - Som2ny Network\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=84481#primaryimage\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=84481#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg\",\"datePublished\":\"2025-02-12T19:42:38+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fivemor.com\/?p=84481#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fivemor.com\/?p=84481\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/?p=84481#primaryimage\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg\",\"width\":2560,\"height\":1287},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fivemor.com\/?p=84481#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fivemor.com\/?bp_activities=1\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Server-side Tagging In Google Tag Manager\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fivemor.com\/#website\",\"url\":\"https:\/\/fivemor.com\/\",\"name\":\"Som2ny Network\",\"description\":\"Daily Deals\",\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fivemor.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/fivemor.com\/#organization\",\"name\":\"Som2ny Network\",\"url\":\"https:\/\/fivemor.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"width\":300,\"height\":86,\"caption\":\"Som2ny Network\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"caption\":\"admin\"},\"sameAs\":[\"https:\/\/fivemor.com\"],\"url\":\"https:\/\/fivemor.com\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Server-side Tagging In Google Tag Manager - Som2ny Network","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fivemor.com\/?p=84481","og_locale":"en_US","og_type":"article","og_title":"Server-side Tagging In Google Tag Manager - Som2ny Network","og_description":"Ever since Server-side tagging was publicly announced at SUPERWEEK 2020, Google and the trusted tester community have been hard at work, building something that just might change the landscape of digital analytics for good. Google Tag Manager has now released Server-side tagging into public beta. In this lengthy article, we\u2019ll take a look at what [&hellip;]","og_url":"https:\/\/fivemor.com\/?p=84481","og_site_name":"Som2ny Network","article_published_time":"2025-02-12T19:42:38+00:00","og_image":[{"width":2560,"height":1287,"url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg","type":"image\/jpeg"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"45 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fivemor.com\/?p=84481#article","isPartOf":{"@id":"https:\/\/fivemor.com\/?p=84481"},"author":{"name":"admin","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371"},"headline":"Server-side Tagging In Google Tag Manager","datePublished":"2025-02-12T19:42:38+00:00","mainEntityOfPage":{"@id":"https:\/\/fivemor.com\/?p=84481"},"wordCount":8606,"commentCount":0,"publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"image":{"@id":"https:\/\/fivemor.com\/?p=84481#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg","keywords":["Google","Manager","Serverside","TAG","Tagging"],"articleSection":["Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fivemor.com\/?p=84481#respond"]}]},{"@type":"WebPage","@id":"https:\/\/fivemor.com\/?p=84481","url":"https:\/\/fivemor.com\/?p=84481","name":"Server-side Tagging In Google Tag Manager - Som2ny Network","isPartOf":{"@id":"https:\/\/fivemor.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/fivemor.com\/?p=84481#primaryimage"},"image":{"@id":"https:\/\/fivemor.com\/?p=84481#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg","datePublished":"2025-02-12T19:42:38+00:00","breadcrumb":{"@id":"https:\/\/fivemor.com\/?p=84481#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fivemor.com\/?p=84481"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/?p=84481#primaryimage","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/server-side-tagging-google-tag-manager-scaled.jpg","width":2560,"height":1287},{"@type":"BreadcrumbList","@id":"https:\/\/fivemor.com\/?p=84481#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fivemor.com\/?bp_activities=1"},{"@type":"ListItem","position":2,"name":"Server-side Tagging In Google Tag Manager"}]},{"@type":"WebSite","@id":"https:\/\/fivemor.com\/#website","url":"https:\/\/fivemor.com\/","name":"Som2ny Network","description":"Daily Deals","publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fivemor.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/fivemor.com\/#organization","name":"Som2ny Network","url":"https:\/\/fivemor.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","width":300,"height":86,"caption":"Som2ny Network"},"image":{"@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371","name":"admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","caption":"admin"},"sameAs":["https:\/\/fivemor.com"],"url":"https:\/\/fivemor.com\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/84481","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=84481"}],"version-history":[{"count":0,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/84481\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/media\/84482"}],"wp:attachment":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=84481"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=84481"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=84481"},{"taxonomy":"dealstore","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fdealstore&post=84481"},{"taxonomy":"offerexpiration","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fofferexpiration&post=84481"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}