How to Host Analytics.js Locally with CAOS and Cookie Notice for GDPR

CAOS has been around for a few years now, bathing in all of the glory it accumulated by hosting analytics.js locally. About two months ago, our lives are enriched by the new GDPR laws. For me, it meant getting back to work and make sure CAOS is GDPR compliant as well. I’ve introduced many changes since then. One of the major new features though, is complete compatibility with other Cookie Notice plugins for WordPress. By far the most popular is Cookie Notice for GDPR. Today I will show you how to configure CAOS to work with this plugin. So you can host analytics.js locally and be GDPR compliant at the same time!

How to configure Google Analytics in Compliance with the GDPR

Awesome! If you made it this far, that means you told Google to back off and respect your visitors’ privacy. It also means that your website almost complies with the GDPR. Almost. Because in order to fully comply you have to make quite a few modifications to your site’s Google Analytics tracking-code.

Breaking News: The Austrian Data Protection Authority (“Datenschutzbehörde”, “DSB” or “DPA”) has ruled that Austrian website providers using Google Analytics are in violation of the GDPR. Since other European countries are likely to follow in this decision, this post has been updated to include measures needed to keep using Google Analytics in compliance with GDPR.

Configuring your Google Analytics tracking code to comply with the GDPR

To fully comply, a few additional measures need to be taken when using Google Analytics.

Before, implementing the aip parameter into your tracking code would suffice. However, since the ruling in Austria of January, 2022 additional measures need to be taken. Specifically:

  • The last two octets of the IP address need to be aonymized (as opposed to just the last octet)
  • The IP address needs to be anonymized before its sent over to Google Analytics
  • Data needs to be proxied through a server (within the European Union) and fully anonymized before it crosses the border and is sent to Google Analytics
  • Universally unique identifiers (UUID) currently used by Google as an additional method to identify users have to be disabled.

Where possible, I’ll add coding examples of how certain measures should be implemented into your (tracking) code. Unfortunately, for certain measures (e.g. proxying traffic) there is no one size fits all solution.

If you’re not comfortable with (or interested in) coding and/or doing the work yourself, upgrade to CAOS Pro and you’ll be all set within minutes.

5. Anonymize your visitor’s IP-address properly

Google Analytics created an option to remove the last octet (the last group of 3 numbers) from your visitor’s IP-address. This is called ‘IP Anonymization‘. This isn’t actual anonymization, because Google takes care of the anonymization for you — on their servers.

To fully comply you’ll want to make sure the IP address is masked before its sent over to Google Analytics’ API.

Lastly, only replacing the last octet of an IP address isn’t considered anonymous enough by Privacy watchdogs. Proper anonymization means replacing the last two octets of an IP address.

This procedure also comes in handy if you want to use Analytics without prior consent from your visitors. However, some countries (e.g. Germany) demand IP’s to be anonymized at all times.

Here’s an example of how to achieve this in PHP:

$octets = explode('.', $ip);
* Instead of using Regex and str_replace, we're slicing the array parts
* and rebuilding the ip (implode) to make sure no duplicate values are
* replaced.
* E.g. using str_replace or preg_replace; would result in
$second_to_last = array_slice($octets, -2, 1, true);
$second_to_last_key = array_key_first($second_to_last);
$second_to_last[$second_to_last_key] = '0';
$last = array_slice($octets, -1, 1, true);
$last_key = array_key_first($last);
$last[$last_key] = '0';
* Replace each octet with the with the
$octets = array_replace($octets, $second_to_last, $last);
return implode('.', $octets);

This script detects all the parts of the IP address and replaces the third and fourth octet with a zero.

Unfortunately, there’s no one size fits all solution for this, but if you’re a WordPress user, you’re in luck!

With CAOS Pro, masking IP addresses and proxying Google Analytics traffic is a matter of a few clicks!

CAOS Pro True IP Anonymization masks the IP address before sending it to Google Analytics
With Anonymize IP and Stealth Mode enabled in CAOS Pro, the IP addresses will be masked before sending them to Google Analytics.

Within CAOS Pro’s settings page (Settings > Optimize Analytics) do the following:

  1. Under Basic Settings click Anonymize IP
  2. Click Save Changes & Update

With Stealth Mode disabled, CAOS Pro will add the aip parameter provided by Google into your tracking code.

To use Google Analytics in compliance with the GDPR, the aip parameter doesn’t suffice and a proxy is required to truly anonymize the IP address before it’s sent to Google Analytics’ servers.

Using a proxy for your Google Analytics traffic has a huge bonus side effect! It also bypasses ad blockers so you’ll get a 100% accurate representation of your website’s traffic!

6. Proxy all traffic to Google Analytics with Stealth Mode

Modifying Google Analytics’ traffic code to intercept its traffic isn’t very hard. By creating a sendHitTask, we can reroute the traffic to our own custom proxy file:

ga('create', 'UA-XXXXXX-Y', 'auto');
// Insert this function after the property is created and before the pageview is sent.
ga(function(tracker) {
tracker.set('sendHitTask', function(model) {
payload = model.get('hitPayload');
var xhr = new XMLHttpRequest();'POST', '', true);
ga('send', 'pageview');

A proxy could simply be anything from a single script to an API endpoint. As long as it captures the data and modifies it according to the specified requirements before passing it to Google Analytics’s Measurement Protocol. It could also be a custom API endpoint (much like CAOS’ Stealth Mode.)

To anonymize all data before it’s sent to Google Analytics you could should use CAOS Pro. To enable its proxy, do the following:

  1. Go to Extensions
  2. Enable Stealth Mode (Pro)
  3. Check if cURL is enabled on your server by clicking the link in the description of the Request Handling option.
  4. If cURL is succesfully detected, choose the Fast (Super Stealth API) option and click Save Changes & Update. Otherwise leave it to Default (WordPress API)

At this point, CAOS Pro will use its built-in (on-premise) API to proxy all traffic to Google Analytics.

7. Lose the UUID trail created by Google

As an additional method to further identify users, Google uses a universally unique identifier. Even with the IP address masked, Google will still be able to identify users if this UUID is still stored somewhere in a browser cookie.

To achieve this, your Google Analytics instance needs to be made cookieless and should be able to generate and store its own UUIDs (unknown to Google)

Are you a CAOS Pro user? Then you’re in luck! By simply enabling Cookieless Analytics (Pro) under CAOS Pro’s Advanced Settings you’ve taken the final measure needed to keep using Google Analytics in compliance with the GDPR. Get CAOS Pro now!

While this measure is meaningless on its own, it is an important step. Disabling cookies in Google Analytics will assure you and your users that any UUID’s stored in cookies will never be used again.

In order to disable cookies, include the storage parameter into your Google Analytics tracking code:

ga('create', 'UA-XXXXX-Y', {
'storage': 'none'

Now we’ve made sure the UUID Google uses to identify the user is lost, we need to feed Google a random one, because the uid is a required parameter in Google Analytics requests.

To activate UUID randomization, include the following before your Google Analytics tracking code:

const cyrb53 = function(str, seed = 0) {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
let creationTime = new Date().getTime();
let expiryTime = creationTime + (30 * 24 * 3600 * 1000); // Change 30 to any number of days you want the UUID to be valid.
let clientIDSource = + ";" + navigator.userAgent + ";" + navigator.language + ";" + creationTime;
// Only store client ID and its expiry time if it doesn't already exist and isn't expired yet.
if (window.localStorage) {
clientIDhashed = localStorage.getItem('GA_CLIENT_ID_HASHED');
clientIDexpiry = localStorage.getItem('GA_CLIENT_ID_EXPIRY');
// Start a fresh session or update when expired. Otherwise, do nothing.
if ((clientIDhashed === null && clientIDexpiry === null)
|| (clientIDhashed !== null && clientIDexpiry !== null && clientIDexpiry >= expiryTime)) {
localStorage.setItem('GA_CLIENT_ID_HASHED', cyrb53(clientIDSource).toString(16));
localStorage.setItem('GA_CLIENT_ID_EXPIRY', expiryTime);

This script is a modified version of Helge Klein’s cookieless tracking script and includes a few optimizations and tweaks:

  • Instead of the IP address (as a truly unique factor in the hash) it uses a UNIX timestamp.
  • To avoid (page) caching collisions, it uses the browser’s localStorage object to guarantee proper tracking of sessions and returning visitors.
  • It includes proper logic to set an expiry time for the generated UUID.

In the previous code snippet, the UUID is stored for 30 days and is stored using the browser’s localStorage. This avoids unexpected collisions with (page) caching mechanisms and allows us to track users (and returning visitors) over an extended period of time.

Then make sure the generated UUID is set as the clientId in Google Analytics.

// Check if localStorage is present. It usually is.
if (window.localStorage) {
ga('create', 'UA-XXXXX-Y', {
'storage': 'none',
'clientId': localStorage.getItem('GA_CLIENT_ID_HASHED')
} else {
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');

After this, you’ve taken the necessary measures to keep using Google Analytics in compliance with the GDPR. Even after the ruling of January, 2022.

8. Inform about Analytics usage and how to opt-out

At this point you’re allowed to process your visitors’ data without their prior consent. You still need to inform them about it and (optionally) allow them to opt-out.

Informing your visitors comes down to adding a Privacy Policy to your site. I could give you a full walkthrough on this, but I think that’s beyond the scope of this article. You can generate a privacy policy for free and if you made it this far into this tutorial, I suppose you know to how to copy + paste the text to a new page in WordPress and put it in your blog’s menu. 🙂


Now you can keep using Google Analytics in compliance with the GDPR, and as a bonus your bypassing ad blockers to further complete your Google Analytics data.

Since you’re no longer collecting personal data, it’s not required to request prior consent, unless you’re still collecting personal data using other servers.

You’ve configured Google Analytics to respect your users’ privacy and CAOS Pro has helped you to truly anonymize user data before its sent over to Google Analytics using a proxy. Was this tutorial helpful? Do you have any suggestions for fellow readers? Did I miss anything? Please let me know in the comments!

How to use CAOS and Analytics with a Cookie Notice (GDPR)

Since the 25th of May, our (online) lives have been blessed with a new and improved GDPR. You might’ve noticed my shiny new Cookie Notice! For novice users it raised a lot of questions. Because as a law-abiding citizen, what the hell do I have to do to keep a good, law-abiding website? If you’re a Google Analytics user, look no further. Today I’m going to show you how you can use CAOS (and all of its locally hosted analytics.js goodness!) to make your WordPress-blog GDPR-proof.

How to Setup a Reverse Proxy in OMV with Let’s Encrypt SSL for Sabnzbd, Radarr, Sonarr and Transmission

In the previous pages of this tutorial I’ve shown you how you can make the OMV plugins Transmission, SABnzbd, Sonarr and Radarr accessible over the web with a Let’s Encrypt SSL-encrypted Reverse Proxy.

If you’re not into how-to’s or you’re not a stranger to Nginx and Terminals, I included the complete configuration file on this page. So you can start digging into it yourself.

A Complete Configuration-file for a Reverse Proxy in OpenMediaVault

This file should be located under /etc/nginx/openmediavault-webgui.d. Make sure to replace with your NAS’ web address and the IP with your NAS’ local IP.

filename: apps.conf

location /sabnzbd {
proxy_pass https://localhost:9080/sabnzbd;
location /sonarr {
proxy_pass http://localhost:8989/sonarr;
location /radarr {
proxy_pass http://localhost:7878/radarr;
location /transmission {
proxy_pass http://localhost:9091/transmission;
location /nginx_status {
stub_status on;
access_log off;
view raw apps.conf hosted with ❤ by GitHub

How to add Social Share Buttons to WordPress without Plugins

There are plenty of plugins for WordPress that allow you to add social share buttons to your Posts and Pages. Usually, these plugins are bloated.

They require a lot of CSS-overrides to make it fit with your theme’s design. Some even slow your site down, because they’re not coded well or are packed with a bunch of options you don’t need.

Well, to hell with code-bloat! I want your site to be fast. Today I’m going to show you a really simple way to add your own social share buttons to your WordPress blog.

Redirect Simple Products to Configurable Parents with Options Preselected

Recently I met quite a challenge while building an extension for Magento. By default, the urls of simple child products whom belong to a configurable parent are answered with a 404. This behaviour can be easily overridden with a small extension.

Our client wanted to take it a step further. Today we will build an extension that redirects URLs of simple products to their configurable parents with options preselected.

How to get a 100/100 Score on Pingdom & Google Pagespeed Insights

This document contains outdated information.

I’ve revisited this topic recently, containing more relevant and up-to-date information aligning with the new specifications provided by Pingdom and Pagespeed Insights. If you’re looking for an up-to-date, quick and easy way to achieve a 100/100 on Pagespeed Insights and Pingdom. I suggest you follow that link.

When you’ve reached this point of this post, it might be because you haven’t still reached that perfect score on Google Pagespeed Insights or Pingdom. This can be because of very site specific issues.

That’s why I made a list of anything I ever ran into while optimizing a WordPress website. You might never make it to that perfect score, because your site is simply too dynamic. But only after you tried everything in the list below, you’ll know you did everything you could.

Good luck!

Pro-tips for Optimizing your WordPress website

Leverage Browser Caching: Host ga.js or analytics.js Locally

There’s a good chance you’re using Google Analytics to keep an eye on your website’s traffic. If so, then there’s no doubt you’re still getting the leverage browser caching suggestion from Google Pagespeed Insights and Pingdom. This sucks! Why would Google have Pagespeed Insights and set the caching rules of its own scripts to only 2 hours?

Appearantly they tweak the analytics script a lot. Almost daily. So it’s necessary that a browser grabs the latest version at all times. Understandable. But 2 hours!?

Thanks to yours truly, the WordPress Plug-ins catalogue now offers the perfect solution. Host ga.js or analytics.js locally (on your server) and update the script using a custom PHP-script and run that cronjob at a certain interval. [ Click the link. I promise it’ll be worth it. I won’t tell anyone. ]

Eliminate render-blocking JavaScript and CSS in above-the-fold content: Save (Google) Webfonts Locally

Just like with the previous pro-tip, Google is the hero and the villain here as well. If you’re using Google’s (or any other) webfonts, you will still be getting the eliminate render-blocking in above-the-fold content suggestion from Google Pagespeed. Pingdom might tell you to leverage browser cache and to combine external CSS. There’s a lot of ground to win with this one, yet the sollution is so simple.

As with the Analytics-script, webfonts can be saved locally as well. You just need to download it, generate a webfont and adjust your CSS accordingly. You can find guides on how to do this in every corner of the web. To make it even easier for you, I’ve created a plugin which will automate hosting Google Fonts locally.

Combine External Javascript: Disable Emoji’s

I would recommend this to anyone, because I don’t think anyone uses this feature in WordPress. Yet it is hard-coded in WordPress and can’t be disabled from within the settings.

wp-emoji.js is a script that can’t be aggregated by Autoptimize or any other minify plugin, which will always result in an extra DNS request and render-blocking content. Luckily this script can be easily disabled. Again, I would recommend this to every WordPress-site.

Reduce Server Response Time: Use Alternative PHP Cache

If you own a VPS or Dedicated Server, you should always have APC enabled. In some cases (such as mine) shared hosts also support APC. Enabling APC from within your servers administration panel (usually cPanel or DirectAdmin) will greatly reduce your server‘s response time. A guide on how to enable APC on a Shared Hosting package (if your host supports it) is coming soon…

Reduce Server Response Time: Use the latest PHP version

Recently PHP 7.0 was released to the public. Most hosts have silently upgraded your hosting package to support it as well. Version 5.6 is still the most widely supported version, but if your WordPress installation allows it, I greatly recommend you upgrade to version 7.0. It might reduce your server‘s response time with about 0.5 seconds. Look around in your hosts admin-panel and/or look into your admin-panel’s documentation. Before switching to PHP 7+, make sure your WordPress theme and plugins support PHP 7+.

Shopping Cart
  • Your cart is empty.