{"id":172004,"date":"2025-04-05T08:33:45","date_gmt":"2025-04-05T08:33:45","guid":{"rendered":"https:\/\/peraltafinancing.com\/analytics\/block-internal-traffic-with-google-tag-manager\/"},"modified":"2025-04-05T08:33:45","modified_gmt":"2025-04-05T08:33:45","slug":"block-internal-traffic-with-google-tag-manager","status":"publish","type":"post","link":"https:\/\/fivemor.com\/?p=172004","title":{"rendered":"Block Internal Traffic With Google Tag Manager"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>You\u2019ve probably come across a <a href=\"http:\/\/www.seerinteractive.com\/blog\/best-ways-to-exclude-internal-traffic-in-google-analytics\">number<\/a> of <a href=\"http:\/\/andygibson.us\/2013\/09\/filter-internal-traffic-using-custom-variables\/\">guides<\/a> or posts talking about why it\u2019s necessary to block so-called <em>internal traffic<\/em> from your web analytics reports. The reasons are pretty solid: internal traffic does not emulate normal visitor behavior, it rarely contributes to conversions (skewing up your conversion rate), it inflates page views, and it wreaks havoc on your granular, page-by-page data.<\/p>\n<p>Internal traffic is vaguely described as \u201cyour employees\u201d, \u201cpeople really close to your brand\u201d, \u201cyour marketing department\u201d, \u201cyour web editors\u201d, and so on. Basically, it should be a term which covers <em>traffic that does not adequately represent trending visitor behavior on your site<\/em>. Most often, this is \u201cinternal\u201d, in that it is traffic by people who generate the content. It can also be your proof-readers (wives, husbands, best friends), beta testers (wives, husbands, best friends), outreach marketers (wives, husb\u2026 you get my drift).<\/p>\n<p>In this post, I\u2019ll introduce two methods to annotate this kind of internal traffic using (mostly) Google Tag Manager. The underlying premise is that your internal traffic comes from such a diverse number of sources that it\u2019s impossible to filter it using Google Analytics\u2019 own filters. Another possibility is that you\u2019ve decided to <a href=\"https:\/\/support.google.com\/analytics\/answer\/2763052?hl=en\">anonymize IP data<\/a> sent to Google Analytics servers, which means that even if you\u2019d just have a certain range of IPs, you couldn\u2019t use Google Analytics\u2019 filters, since the last octet has been censored (more about this later).<\/p>\n<p>By the way, I use the term <em>annotate<\/em> instead of <em>block<\/em> (except in the search engine friendly title, GUILTY!), because whether or not you actually want to block the data or just segment it is up to you. You could also follow <a href=\"https:\/\/www.bounteous.com\/insights\/2014\/03\/11\/goodbye-to-exclude-filters-google-analytics\/\">Bounteous\u2019 excellent guide<\/a> on preventing the data from being sent to Google Analytics in the first place, but I wanted to follow a more reconciliatory route, offering a way for you to still incorporate internal traffic in your reports, if you so choose. And this route is, of course, a <strong>custom dimension<\/strong>.<\/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=\"the-premise\">The premise<\/h2>\n<p>I will introduce two methods: 1) Visit to URL, and 2) IP extraction. Both have their pros and cons.<\/p>\n<p>If the people you want included in your <em>internal traffic<\/em> are constantly on the move, use a variety of devices on the road, or are hard to pin down just by using an IP address range, you should use the first method. This way you\u2019ll also leave these people an out. You see, sometimes your employees are also your customers or normal site visitors, which means that their behavior should be included in your actual traffic reports.<\/p>\n<p>IP extraction requires that you have specific IP address ranges in mind which you want to block. This is best for traffic which originates from stationary places, like office buildings or office networks.<\/p>\n<p>Whichever you choose, you\u2019ll need to set up a <strong>custom dimension<\/strong> first, which will store your data. It\u2019s really simple, and Google has <a href=\"https:\/\/support.google.com\/analytics\/answer\/2709829?hl=en\">a great guide<\/a> on how to get started.<\/p>\n<p>If you want to provide your internal traffic an out, you should create a <strong>session-scope<\/strong> custom dimension. This way internal traffic is annotated separately from one session to the next.<\/p>\n<div style=\"aspect-ratio: 455 \/ 275;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2014\/03\/custom-dimension.jpg\" title=\"Create custom dimension either session or user scope\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"275\" width=\"455\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2014\/03\/custom-dimension.jpg#ZgotmplZ\" alt=\"Create custom dimension either session or user scope\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>If, however, you want the annotation to stick \u201cfor life\u201d, you should create a <strong>user-scope<\/strong> custom dimension (<em>user<\/em> is the same as <em>device<\/em>). This way the annotation will persist from session to session, even if the IP extraction fails or the user doesn\u2019t visit your designated \u201cinternal traffic\u201d URL.<\/p>\n<h2 id=\"method-1-visit-to-url\">Method 1: Visit to URL<\/h2>\n<p>I really like this method, since it\u2019s completely client-side, it doesn\u2019t require any custom functions or external API calls to retrieve the IP address, and it provides an out for your employees or users, if they wish to be treated as external traffic.<\/p>\n<p>One thing that it does require, however, is that your internal traffic remembers to use the URL every single time they visit your site (unless they don\u2019t want to be treated separately). Since you\u2019re using a session-scope dimension here, it\u2019s ok if the internal traffic URL is not the <em>landing page<\/em> of the visit. A session-scope custom dimension hit is applied retroactively to all hits in the session, so as long as the custom dimension is sent with a single hit during the session, it will suffice to annotate all hits in the session.<\/p>\n<p>I prefer to use a <strong>URL query parameter<\/strong> as the trigger that tags the visitor as internal traffic. This means that:<\/p>\n<ol>\n<li>\n<p>You need to make sure that your server accepts query parameters in URL addresses<\/p>\n<\/li>\n<li>\n<p>You should canonicalize all pages to the parameter-less version, so that search engines won\u2019t index your internal traffic page by accident (use <code><link rel=\"&quot;canonical&quot;\" href=\"&quot;http:\/\/url-of-page-without-query-parameters&quot;\/\"\/><\/code>)<\/p>\n<\/li>\n<li>\n<p>You should configure your Google Analytics view so that this query parameter is stripped from reports (because it doesn\u2019t add any value content-wise)<\/p>\n<\/li>\n<\/ol>\n<p>For this example, I\u2019ve chosen the query <strong>internal=true<\/strong> as the trigger. So if you navigate to the homepage, you\u2019d need to use <code>http:\/\/www.mydomain.com\/?internal=true<\/code> to be tagged as internal traffic.<\/p>\n<p>Here\u2019s how you use the query parameter as a custom dimension in your tags.<\/p>\n<ol>\n<li>\n<p>Create a new macro (Macro Name: <strong>Internal URL<\/strong>, Macro Type: <strong>URL<\/strong>, Component Type: <strong>Query<\/strong>)<\/p>\n<\/li>\n<li>\n<p>In the field <em>Query Key<\/em>, add <strong>internal<\/strong><\/p>\n<\/li>\n<\/ol>\n<div style=\"aspect-ratio: 280 \/ 447;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2014\/03\/internal-url.jpg\" title=\"Internal traffic URL macro\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"447\" width=\"280\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2014\/03\/internal-url.jpg#ZgotmplZ\" alt=\"Internal traffic URL macro\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>When called, this macro looks at your URL, tries to find the query parameter \u201cinternal\u201d, and if it does, it returns its value (\u201ctrue\u201d). If no parameter is found, it returns an <em>undefined<\/em> which suits us just fine.<\/p>\n<p>Next, you\u2019ll need to send this along with your page view tag.<\/p>\n<ol>\n<li>\n<p>Open your page tracking tag<\/p>\n<\/li>\n<li>\n<p>Go to <strong>More Settings<\/strong> \u00bb <strong>Custom Dimensions<\/strong><\/p>\n<\/li>\n<li>\n<p>Add the index number of your custom dimension in the appropriate slot<\/p>\n<\/li>\n<li>\n<p>Add <strong>{{Internal URL}}<\/strong> into the <em>Dimension<\/em> field<\/p>\n<\/li>\n<\/ol>\n<div style=\"aspect-ratio: 526 \/ 438;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2014\/03\/custom-dimension-pageview.jpg\" title=\"Internal URL Custom dimension\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"438\" width=\"526\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2014\/03\/custom-dimension-pageview.jpg#ZgotmplZ\" alt=\"Internal URL Custom dimension\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>And <strong><em>that\u2019s it<\/em><\/strong>! The beauty of Google Tag Manager macros is that you don\u2019t need a separate page view tag for undefined custom dimensions. You can just use this one macro. If the query parameter \u201cinternal\u201d is found, its value (\u201ctrue\u201d) is sent as a custom dimension along with your page view hit. However, if no such parameter is found, <em>no custom dimension is sent<\/em>, so you don\u2019t have to worry about overwriting your data with an empty custom dimension or something.<\/p>\n<p>Finally, check with a debugger like <a href=\"http:\/\/webanalyticssolutionprofiler.com\/\">WASP<\/a> that your custom dimension is actually being sent over.<\/p>\n<h3 id=\"pro-tip-use-a-url-rewrite\">Pro tip, use a URL rewrite<\/h3>\n<p>If you or your internal traffic users find that using a URL query parameter is tedious, you should set up a 301 server-side redirection, and use either a vanity URL or a subdomain to handle the redirection. So if <code>http:\/\/www.mydomain.com\/?internal=true<\/code> is just too difficult to manage, make it so that <code>http:\/\/www.mydomain.com\/office<\/code> or <code>office.mydomain.com<\/code> redirect the user to the home page with the query parameter in place (or both!). If you have an Apache server, you could try something like this for the subdomain redirect (check with IT first!):<\/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-ApacheConf\" data-lang=\"ApacheConf\"><span style=\"color:#0aa\">RewriteEngine<\/span> <span style=\"color:#00a\">on<\/span>\n<span style=\"color:#0aa\">RewriteCond<\/span> %{HTTP_HOST} ^office\\.mydomain\\.com$\n<span style=\"color:#0aa\">RewriteRule<\/span> ^$ http:\/\/www.mydomain.com\/?internal=true [R=301,L]<\/code><\/pre>\n<\/div>\n<p>A popular way to separate internal traffic from external traffic is to use the client\u2019s IP address. The most common method is to use Google Analytics\u2019 standard features to filter out any IP addresses that are within a specified range. Because this is so \u201cstandard\u201d, I won\u2019t go into it (you can read the <a href=\"https:\/\/support.google.com\/analytics\/answer\/1034840?hl=en\">official words here<\/a>).<\/p>\n<p>This guide is for <em>annotating<\/em> internal traffic, not for filtering it, so you\u2019ll be checking the client\u2019s IP address against a specified range, and if there\u2019s a match, you\u2019ll send this information via custom dimension.<\/p>\n<p>(<strong>Pro tip<\/strong>: This is especially useful if you have <strong>IP anonymization<\/strong> set up. In Google Analytics, IP anonymization censors the last octet of the client\u2019s IP (111.222.333.<strong>XXX<\/strong>) and sets it to 0 (111.222.333.<strong>0<\/strong>). This is what\u2019s sent to Google Analytics servers, ensuring a higher level of security, if you don\u2019t want the geeks at Mountain View looking at your visitors\u2019 IP addresses.)<\/p>\n<p>To extract the client IP, you have a number of choices. The accuracy will vary greatly, since if the visitor is using a proxy, for example, it\u2019s not really their IP that\u2019s retrieved but the proxy\u2019s. In this guide, I\u2019ll introduce you to a simple PHP value retrieval, and also how to retrieve the IP with JavaScript using an external API call.<\/p>\n<h3 id=\"retrieve-and-process-the-ip-with-php\">Retrieve and process the IP with PHP<\/h3>\n<p>This is one way to do it, and it should work if you have a site which is parsed with PHP (WordPress, Drupal, etc.). In your page template, before your container snippet, you create the dataLayer object with <strong>window.dataLayer = window.dataLayer || [];<\/strong>. Use this to enter the client IP into the dataLayer object as it is created. This way it will be ready when the container is set up and your page views are sent:<\/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:#0aa\">window<\/span>.dataLayer = <span style=\"color:#0aa\">window<\/span>.dataLayer || [];\n  <span style=\"color:#0aa\">window<\/span>.dataLayer.push({\n    <span style=\"color:#a50\">\"ipaddress\"<\/span>: <span style=\"color:#a50\">\"<?php echo $IPADDRESS ??>\"<\/span>\n  });\n<\/code><\/pre>\n<\/div>\n<p>This will retrieve the IP address using the designated PHP variable, and it will store it in the data layer variable <strong>ipaddress<\/strong>.<\/p>\n<p>This is a great way to extract the IP address, since you don\u2019t have to wait for an external API to resolve the address. <a href=\"http:\/\/techtalk.virendrachandak.com\/getting-real-client-ip-address-in-php-2\/\">Here\u2019s a more thorough guide<\/a> on retrieving the IP with a PHP call.<\/p>\n<p>Next, create a Data Layer Variable Macro through which you can process the IP:<\/p>\n<ol>\n<li>\n<p>Create new macro (Macro Name: <strong>Retrieve IP<\/strong>, Macro Type: <strong>Data Layer Variable<\/strong>)<\/p>\n<\/li>\n<li>\n<p>Set <strong>ipaddress<\/strong> as the Data Layer Variable Name<\/p>\n<\/li>\n<li>\n<p>Set <strong>none<\/strong> as the Default Value<\/p>\n<\/li>\n<\/ol>\n<div style=\"aspect-ratio: 329 \/ 502;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2014\/03\/retrieve-ip-dlv.jpg\" title=\"Retrieve IP macro DLV\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"502\" width=\"329\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2014\/03\/retrieve-ip-dlv.jpg#ZgotmplZ\" alt=\"Retrieve IP macro DLV\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>(Why the default value, you ask? Well I like dealing with declared entities, so that I won\u2019t have to check for <em>undefined<\/em> every single time I want to process a variable.)<\/p>\n<p>Next, you\u2019ll need a Custom HTML tag in which you\u2019ll process the IP address. The aim is to push a data layer variable \u201cinternal\u201d: \u201ctrue\u201d, if the IP matches a given address or range.<\/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-html\" data-lang=\"html\">script&gt;\n  <span style=\"color:#00a\">var<\/span> ipaddress = {{Retrieve IP}}; <span style=\"color:#aaa;font-style:italic\">\/\/ Retrieves the IP from the data layer\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>\n  <span style=\"color:#aaa;font-style:italic\">\/\/ Comment following three lines if you want to use the IP range method\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#00a\">if<\/span> (ipaddress == <span style=\"color:#a50\">\"111.222.333.444\"<\/span>) {\n    dataLayer.push({<span style=\"color:#a50\">\"internal\"<\/span>: <span style=\"color:#a50\">\"true\"<\/span>});\n  }\n\n  <span style=\"color:#aaa;font-style:italic\">\/\/ To use the following IP range check, comment the previous three lines\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#aaa;font-style:italic\">\/\/ of code and uncomment the following lines\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#aaa;font-style:italic\">\/\/\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#aaa;font-style:italic\">\/\/  var ipRange = ipaddress.split(\".\");\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#aaa;font-style:italic\">\/\/  var lastOctet = parseInt(ipRange.pop());\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#aaa;font-style:italic\">\/\/  if(lastOctet &gt;= 1 &amp;&amp; lastOctet <span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#aaa;font-style:italic\">\/\/    dataLayer.push({\"internal\": \"true\"});\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#aaa;font-style:italic\">\/\/  }\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  \n  dataLayer.push({<span style=\"color:#a50\">\"event\"<\/span>: <span style=\"color:#a50\">\"ipComplete\"<\/span>});\n<span style=\"color:#1e90ff;font-weight:bold\">script<\/span>&gt;<\/span><\/code><\/pre>\n<\/div>\n<ul>\n<li>Add Firing Rule to tag: <strong>{{event}} equals gtm.js<\/strong><\/li>\n<\/ul>\n<p>You have two choices in the code above. Either you do an exact match (if your office has just one IP address), or you check against a range. In the range check, I check if the last octet (111.222.333.<strong>XXX<\/strong>) is between 1 and 100, so remember to modify this to match the range you want to check for. And if you have multiple IP addresses you want to include in the check, just modify the if-clause to your liking. You can (and should) also use regular expression pattern matching, if the variations get more diverse.<\/p>\n<p>Whatever you do, this should push the data layer variable \u201cinternal\u201d: \u201ctrue\u201d if the IP address matches a given pattern. Finally, you push a trigger event \u201cipComplete\u201d, which is what will fire your page view tag, with which the custom dimension is also passed along.<\/p>\n<p>The Firing Rule here is <strong>{{event}} equals gtm.js<\/strong> to ensure that the code is run at the earliest possible opportunity, to avoid delaying the page view call any more than necessary.<\/p>\n<h3 id=\"retrieve-and-process-the-ip-with-javascript\">Retrieve and process the IP with JavaScript<\/h3>\n<p>If you can\u2019t use PHP or server-side scripting, you can use JavaScript. Well, you can\u2019t <em>technically<\/em> resolve the client IP using just JavaScript. You need to request the IP from an external resource such as <a href=\"http:\/\/hostip.info\">Hostip.info<\/a> or <a href=\"http:\/\/www.geoplugin.com\">GeoPlugin<\/a>. This means that this approach is a bit more suspect, because you have to trust the request endpoint to serve you with the correct data every time. Nevertheless, if you can\u2019t retrieve the IP server-side, this is what you should try.<\/p>\n<p>I use Hostip.info in my example, but there are a number of APIs out there that you can use. I suggest you find an API that allows you to retrieve the data with an asynchronous AJAX request (you could also use an XMLHttpRequest), and which returns the data as a JSON object that you can then parse. This way your contraption won\u2019t go up in flames, taking most of your website along in its carnival of destruction, if the endpoint chooses to change the way it distributes.<\/p>\n<p>I\u2019ve added the AJAX call to the Custom HTML Tag where I\u2019ll be doing the pattern matching as well.<\/p>\n<p><strong>NOTE!<\/strong> This script requires that you have loaded <a href=\"https:\/\/developers.google.com\/speed\/libraries\/devguide#jquery\">jQuery<\/a> before this script is run. So make sure the library is loaded first.<\/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-html\" data-lang=\"html\">script&gt;\n  <span style=\"color:#00a\">function<\/span> matchIP(ipaddress) {\n    <span style=\"color:#aaa;font-style:italic\">\/\/ Comment following three lines if you want to use the IP range method\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#00a\">if<\/span> (ipaddress == <span style=\"color:#a50\">\"111.222.333.444\"<\/span>) {\n      dataLayer.push({<span style=\"color:#a50\">\"internal\"<\/span>: <span style=\"color:#a50\">\"true\"<\/span>});\n    }\n\n    <span style=\"color:#aaa;font-style:italic\">\/\/ To use the following IP range check, comment the previous three lines\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#aaa;font-style:italic\">\/\/ of code and uncomment the following lines\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#aaa;font-style:italic\">\/\/\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#aaa;font-style:italic\">\/\/  var ipRange = ipaddress.split(\".\");\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#aaa;font-style:italic\">\/\/  var lastOctet = parseInt(ipRange.pop());\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#aaa;font-style:italic\">\/\/  if(lastOctet &gt;= 1 &amp;&amp; lastOctet <span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#aaa;font-style:italic\">\/\/    dataLayer.push({\"internal\": \"true\"});\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#aaa;font-style:italic\">\/\/  }\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  }\n  \n  <span style=\"color:#00a\">var<\/span> ipaddress = <span style=\"color:#a50\">\"\"<\/span>;\n  <span style=\"color:#00a\">try<\/span> {\n    jQuery.ajax({\n      type : <span style=\"color:#a50\">\"GET\"<\/span>,\n      dataType : <span style=\"color:#a50\">\"json\"<\/span>,\n      url : <span style=\"color:#a50\">\"http:\/\/api.hostip.info\/get_json.php\"<\/span>,\n      async : <span style=\"color:#00a\">true<\/span>,\n      success : <span style=\"color:#00a\">function<\/span>(data) {\n        ipaddress = data.ip;      \n      },error : <span style=\"color:#00a\">function<\/span>(errorData) {\n        ipaddress = <span style=\"color:#a50\">\"none\"<\/span>;\n      },complete : <span style=\"color:#00a\">function<\/span>() {\n        matchIP(ipaddress);\n      }\n    });\n  } <span style=\"color:#00a\">catch<\/span>(e) {\n    console.log(<span style=\"color:#a50\">\"Oops, something went wrong: \"<\/span> + e.message);\n  }\n  dataLayer.push({<span style=\"color:#a50\">\"event\"<\/span>: <span style=\"color:#a50\">\"ipComplete\"<\/span>});\n<span style=\"color:#1e90ff;font-weight:bold\">script<\/span>&gt;<\/span><\/code><\/pre>\n<\/div>\n<ul>\n<li>Set Firing Rule for tag <strong>{{event}} equals gtm.js<\/strong><\/li>\n<\/ul>\n<p><strong>Phew!<\/strong><\/p>\n<p>In this tag, you send an asynchronous HTTP POST request to the endpoint at hostip.info. The request returns a JSON script, which you parse for the node \u201cip\u201d. The value stored in this node is then stored in the data layer variable <strong>ipaddress<\/strong>. Finally, as soon as the request is complete, you call a function called <strong>matchIP()<\/strong>, where the actual parsing takes place.<\/p>\n<p>The parsing is identical to what we did in the PHP IP retrieval script, so you can either evaluate an exact match or a range of IP addresses. With small modifications you can check versus multiple addresses and ranges, if you so choose.<\/p>\n<p>Again, the desired end result is a new data layer variable \u201cinternal\u201d, which should have the value \u201ctrue\u201d, if traffic is internal. It should remain undefined if traffic is not internal, or if an error crops up in your AJAX request.<\/p>\n<p>And again, there\u2019s the trigger event \u201cipComplete\u201d, which is the firing rule for your page view, which will be the vessel for your custom dimension.<\/p>\n<p>The firing rule for this Custom HTML Tag is <strong>{{event}} equals gtm.js<\/strong>, because you want to make sure that the script is run at the earliest possible moment to avoid delaying your page view call any more than necessary.<\/p>\n<h3 id=\"send-custom-dimension-with-page-view-tag\">Send custom dimension with page view tag<\/h3>\n<p>And, finally, we are about to reach the end of this arduous journey. It\u2019s time to modify your page view tag, so that it includes information about the traffic type in a custom dimension.<\/p>\n<p>First, create a Data Layer Variable Macro for your internal traffic.<\/p>\n<div style=\"aspect-ratio: 329 \/ 505;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2014\/03\/internal-ip.jpg\" title=\"Internal IP macro\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"505\" width=\"329\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2014\/03\/internal-ip.jpg#ZgotmplZ\" alt=\"Internal IP macro\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Now we\u2019re ready to edit the page view tag.<\/p>\n<ol>\n<li>\n<p>Open your page view tag<\/p>\n<\/li>\n<li>\n<p>Go to <strong>More Settings<\/strong> \u00bb <strong>Custom Dimensions<\/strong><\/p>\n<\/li>\n<li>\n<p>Add a new Custom Dimension, and give it the correct index number<\/p>\n<\/li>\n<li>\n<p>In the <strong>Dimension<\/strong> slot, add your new data layer variable macro (<strong>{{Internal IP}}<\/strong>)<\/p>\n<\/li>\n<li>\n<p>Add Firing Rule: <strong>{{event}} equals ipComplete<\/strong><\/p>\n<\/li>\n<\/ol>\n<div style=\"aspect-ratio: 856 \/ 518;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2014\/03\/pageview-tag.jpg\" title=\"Page view tag\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"518\" width=\"856\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2014\/03\/pageview-tag.jpg#ZgotmplZ\" alt=\"Page view tag\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>That\u2019s it! Your page view tag obediently waits for the IP check to complete. Once the check is made, if traffic was internal, the value \u201ctrue\u201d is sent as a custom dimension with the page view tag.<\/p>\n<p><strong>NOTE!<\/strong> Making your page view tag wait for any scripts (especially external API calls) is a bit dangerous, since if there\u2019s a significantly long load time, your page view might not get sent at all. You might want to leave your page view tag as it is, and send the <a href=\"https:\/\/www.simoahava.com\/analytics\/universal-analytics-send-custom-dimension-event\/\">custom dimension with a non-interaction event<\/a> instead.<\/p>\n<h2 id=\"conclusions\">Conclusions<\/h2>\n<p>I didn\u2019t intend this post to be a be-all and end-all to All Internal Traffic Exclusion Guides on the web, so I\u2019m a bit confused why this ended up being such a long post.<\/p>\n<p>Regardless of my verbosity, I hope this post served to highlight some of Google Tag Manager\u2019s amazing versatility. The combination of macros, tags, and defined \/ undefined custom dimensions creates opportunities for really complex tagging with a really simple setup.<\/p>\n<p>With the help of this post, you can now segment (or filter) your internal traffic in Google Analytics by looking for \u201ctrue\u201d in your Internal Traffic custom dimension.<\/p>\n<p>What other ways have you discovered to identify internal traffic (with or without GTM)? I\u2019d love to add a third method to this tutorial, even though then this will <strong>really<\/strong> be a bloated guide.<\/p>\n<\/p><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>You\u2019ve probably come across a number of guides or posts talking about why it\u2019s necessary to block so-called internal traffic from your web analytics reports. The reasons are pretty solid: internal traffic does not emulate normal visitor behavior, it rarely contributes to conversions (skewing up your conversion rate), it inflates page views, and it wreaks [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":172005,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12033],"tags":[3657,1531,16293,14495,1791,14996],"dealstore":[],"offerexpiration":[],"class_list":["post-172004","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-block","tag-google","tag-internal","tag-manager","tag-tag","tag-traffic"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Block Internal Traffic With 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=172004\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Block Internal Traffic With Google Tag Manager - Som2ny Network\" \/>\n<meta property=\"og:description\" content=\"You\u2019ve probably come across a number of guides or posts talking about why it\u2019s necessary to block so-called internal traffic from your web analytics reports. The reasons are pretty solid: internal traffic does not emulate normal visitor behavior, it rarely contributes to conversions (skewing up your conversion rate), it inflates page views, and it wreaks [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fivemor.com\/?p=172004\" \/>\n<meta property=\"og:site_name\" content=\"Som2ny Network\" \/>\n<meta property=\"article:published_time\" content=\"2025-04-05T08:33:45+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"455\" \/>\n\t<meta property=\"og:image:height\" content=\"275\" \/>\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=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fivemor.com\/?p=172004#article\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/?p=172004\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\"},\"headline\":\"Block Internal Traffic With Google Tag Manager\",\"datePublished\":\"2025-04-05T08:33:45+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=172004\"},\"wordCount\":2622,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=172004#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg\",\"keywords\":[\"Block\",\"Google\",\"Internal\",\"Manager\",\"TAG\",\"traffic\"],\"articleSection\":[\"Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fivemor.com\/?p=172004#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fivemor.com\/?p=172004\",\"url\":\"https:\/\/fivemor.com\/?p=172004\",\"name\":\"Block Internal Traffic With Google Tag Manager - Som2ny Network\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=172004#primaryimage\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=172004#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg\",\"datePublished\":\"2025-04-05T08:33:45+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fivemor.com\/?p=172004#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fivemor.com\/?p=172004\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/?p=172004#primaryimage\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg\",\"width\":455,\"height\":275},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fivemor.com\/?p=172004#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fivemor.com\/?bp_activities=1\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Block Internal Traffic With 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":"Block Internal Traffic With 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=172004","og_locale":"en_US","og_type":"article","og_title":"Block Internal Traffic With Google Tag Manager - Som2ny Network","og_description":"You\u2019ve probably come across a number of guides or posts talking about why it\u2019s necessary to block so-called internal traffic from your web analytics reports. The reasons are pretty solid: internal traffic does not emulate normal visitor behavior, it rarely contributes to conversions (skewing up your conversion rate), it inflates page views, and it wreaks [&hellip;]","og_url":"https:\/\/fivemor.com\/?p=172004","og_site_name":"Som2ny Network","article_published_time":"2025-04-05T08:33:45+00:00","og_image":[{"width":455,"height":275,"url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg","type":"image\/jpeg"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fivemor.com\/?p=172004#article","isPartOf":{"@id":"https:\/\/fivemor.com\/?p=172004"},"author":{"name":"admin","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371"},"headline":"Block Internal Traffic With Google Tag Manager","datePublished":"2025-04-05T08:33:45+00:00","mainEntityOfPage":{"@id":"https:\/\/fivemor.com\/?p=172004"},"wordCount":2622,"commentCount":0,"publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"image":{"@id":"https:\/\/fivemor.com\/?p=172004#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg","keywords":["Block","Google","Internal","Manager","TAG","traffic"],"articleSection":["Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fivemor.com\/?p=172004#respond"]}]},{"@type":"WebPage","@id":"https:\/\/fivemor.com\/?p=172004","url":"https:\/\/fivemor.com\/?p=172004","name":"Block Internal Traffic With Google Tag Manager - Som2ny Network","isPartOf":{"@id":"https:\/\/fivemor.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/fivemor.com\/?p=172004#primaryimage"},"image":{"@id":"https:\/\/fivemor.com\/?p=172004#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg","datePublished":"2025-04-05T08:33:45+00:00","breadcrumb":{"@id":"https:\/\/fivemor.com\/?p=172004#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fivemor.com\/?p=172004"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/?p=172004#primaryimage","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/04\/custom-dimension.jpg","width":455,"height":275},{"@type":"BreadcrumbList","@id":"https:\/\/fivemor.com\/?p=172004#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fivemor.com\/?bp_activities=1"},{"@type":"ListItem","position":2,"name":"Block Internal Traffic With 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\/172004","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=172004"}],"version-history":[{"count":0,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/172004\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/media\/172005"}],"wp:attachment":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=172004"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=172004"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=172004"},{"taxonomy":"dealstore","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fdealstore&post=172004"},{"taxonomy":"offerexpiration","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fofferexpiration&post=172004"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}