Privacy and tracking users
The European Union’s General Data Protection Regulation (GDPR) defines how internet users’ personal data is to be handled by organizations that have European offices as well as non-European organizations that target European users for free or paid goods or services.
You should consult with your legal advisor on whether or not you need a cookie notice, a privacy statement, and/or a way for users to opt-out of tracking.
IP address privacy
In 2016, the Court of Justice of the European Union held that, in certain circumstances, IP addresses are personal data.
This was followed in early 2022 by a decision from a court in Munich that linking to a third-party without prior consent, such as to Google to use Google fonts, violated the EU privacy law.
Based on the prohibition against sharing a user’s IP address, I have:
- switched from using Google fonts to using system fonts;
- switched from sourcing framework and library files (i.e. Bootstrap and jQuery javascript) from content delivery networks (i.e. cdnjs) to self-hosting the files;
- and switched to downloading the JavaScript file for Google Analytics only if the user has agreed to accept tracking cookies.
Google analytics
I use Google Analytics (along with Google Search Console) to understand how my sites are being used. Previously, to enable users to opt-out of their data being sent to Google, I used Google’s ga-disable cookie method, but that method still required the browser to download a JavaScript file from Google to read the cookie that said whether the user had opted out or not. The browser was still sending the user’s IP address to Google in order to get the JavaScript file.
I switched from using Google’s ga-disable cookie method to using either my own cookie or a sessionStorage value. When someone visits the site for the first time, they are presented with a cookie notice where they can either accept cookies (and allow the use of Google Analytics) or they can opt-out.
If the visitor accepts the use of cookies (and the use of Google Analytics), I create a cookie, valid for one year, to record their choice. After that, when they visit the site, the page reads the cookie and does not display the cookie notice.
If the visitor rejects the use of cookies (and the use of Google Analytics), I store a value in sessionStorage to record their choice. After that, when the visitor views any other site page during the current browser session (the browser and the tab remain open), the pages will read the sessionStorage value and not display the cookie notice. If the visitor returns to the site in a different session, they will again be shown the cookie notice.
To make this work, I use three JavaScript code components to manage user tracking. The first is in the head section of the page:
var gaScript;
var googleAnalyticsID = "UA-11155712-3";
gaScript = document.createElement("script");
gaScript.async = true;
gaScript.src = "https://www.googletagmanager.com/gtag/js?id=UA-11155712-3";
// Until Google Tagmanager has been loaded, this code will have no effect
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-11155712-3', { "anonymize_ip": true });
if (document.cookie.indexOf("eu-cookie-notice=true") > -1) {
// User has accepted the terms, so load analytics (not opted out).
document.head.appendChild(gaScript);
}
This code creates a script tag to load the Google Tag Manager JavaScript, and, if the user has previously accepted cookies, the script tag is appended to the page head. This code also sets up a dataLayer array for Google tag information. If the user has or does accept cookies, the tracking information will be sent to Google. If the user rejects tracking, the Google JavaScript will not be downloaded and the tracking information will never be sent.
The second component is in the js-functions file and handles the cookie notice:
// Show the EU cookie notice (if it's the first time to the site or a new session)
if (document.cookie.indexOf("eu-cookie-notice") < 0) { // no cookie
if (window.sessionStorage.getItem("eu-opt-out") !== "true") { //no session item
//Cookie not found and no current session, so show the banner
$("#eu-cookie").slideDown();
$(".detail-first-section").addClass("cookie-notice-margin");
$("#btn-cookie-accept").on("click", function() {
var date = new Date();
// gaScript is defined in the head
date.setTime(date.getTime() + 31536000000); // one year
document.cookie = "eu-cookie-notice=true; expires="
+ date.toGMTString() + ";path=/";
$("#eu-cookie").slideUp();
$(".detail-first-section").removeClass("cookie-notice-margin");
// Enable tracking
document.head.appendChild(gaScript);
});
$("#btn-cookie-opt-out").on("click", function() {
try {
window.sessionStorage.setItem("eu-opt-out", "true");
} catch (error) {
console.error(error);
}
$("#eu-cookie").slideUp();
$(".detail-first-section").removeClass("cookie-notice-margin");
});
}
}
This code checks whether a visitor is new to the site, and if so, shows a banner across the top of the page with information about the site using cookies, with one button for the user to accept cookies and another for the user to opt-out of tracking.
Accepting cookies stores a cookie that the user has accepted, hides the notice, and triggers Google tracking for this visit. The cookie is good for one year, after which the user will need to reaccept cookies.
Opting out of cookies stores a session variable that the user has opted out and hides the notice.
The notice includes a link to the Terms of Use page, which includes privacy information and its own opt-out button. This is the third JavaScript component:
<button type="button" class="btn btn-dark center opt-out" onclick="gaOptOut()">
Opt-out of Cookies & Analytics</button>
function gaOptOut() {
try {
window.sessionStorage.setItem("eu-opt-out", "true");
} catch (error) {
console.error(error);
}
document.cookie = "eu-cookie-notice=; expires=Thu, 01 Jan 1970 00:00:01 GMT;
path=/";
$("#eu-cookie").slideUp();
$(".detail-first-section").removeClass("cookie-notice-margin");
}
The opt-out button stores a session variable that the user has opted out. If the cookie notice is visible, it is hidden, and if there was a cookie, it is removed.
The head template includes code to conditionally load a polyfill for the string.trim JavaScript function. IE 8 and older versions of Internet Explorer don’t support the trim function, which is needed for the Google tag manager. Loading the polyfill prevents any errors.
<script>
// Polyfill for old Internet Explorers
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
}
</script>
Social networking
Social networking sites and apps check how a page should be referenced if someone shares a link to the page.
Open Graph
Most social networking apps support Open Graph:
Twitter Card
Twitter has their own set of tags:
Note: I’m using the page title and description variables ({{ @siteOwner }} – {{ @pageTitle }} & {{ @pageDescription }}) for the social network / twitter title and description. If a different title and/or description is desired, it would be easy to add separate variables to the page controller.