Send reasoning for issue to Vue developers
Issue: https://github.com/vuejs/vuejs.org/issues/2641
Hey guys!
We are considering moving to Netlify Analytics, which is server-side and does no tracking
That sounds like a good idea. Isn't that a paid solution though? @sdras
That said, I'm not an expert on analytics and privacy, so please elaborate if you think our way of using Google Analytics still leads to privacy issues.
I mean, even with just tracking page views, there are still some inherent privacy issues when using Google Analytics. For example, Google Analytics seems to fingerprint users, which is a very dangerous privacy violation.
One example of fingerprinting is Google Analytics' uploading of the screen size of the browser.
Just opening the page in Tor Browser sends a request to Google Analytics' collection endpoint. The JavaScript sends this mysterious data:
{
"Query string": {
"v": "1",
"_v": "j83",
"a": "1398337363",
"t": "pageview",
"_s": "1",
"dl": "https://vuejs.org/",
"ul": "en-us",
"de": "UTF-8",
"dt": "Vue.js",
"sd": "24-bit",
"sr": "2000x600",
"vp": "1988x600",
"je": "0",
"_u": "IEBAAAAB~",
"jid": "791792288",
"gjid": "834920789",
"cid": "183173963.1595265292",
"tid": "UA-46852172-1",
"_gid": "1707634750.1595265292",
"_r": "1",
"z": "1439589530"
}
}
After some quick inspection of the enormous Google Analytics client, I can see this:
var hb = T("apiVersion", "v"),
ib = T("clientVersion", "_v");
S("anonymizeIp", "aip");
var jb = S("adSenseId", "a"),
Va = S("hitType", "t"),
Ia = S("hitCallback"),
Ra = S("hitPayload");
S("nonInteraction", "ni");
S("currencyCode", "cu");
S("dataSource", "ds");
var Vd = S("useBeacon", void 0, !1),
fa = S("transport");
S("sessionControl", "sc", "");
S("sessionGroup", "sg");
S("queueTime", "qt");
var Ac = S("_s", "_s");
S("screenName", "`");
var kb = S("location", "dl", ""),
lb = S("referrer", "dr"),
mb = S("page", "dp", "");
S("hostname", "dh");
var nb = S("language", "ul"),
ob = S("encoding", "de");
var pb = S("screenColors", "sd"),
qb = S("screenResolution", "sr"),
rb = S("viewportSize", "vp"),
sb = S("javaEnabled", "je"),
tb = S("flashVersion", "fl");
S("campaignId", "ci");
S("campaignName", "cn");
S("campaignSource", "cs");
S("campaignMedium", "cm");
S("campaignKeyword", "ck");
S("campaignContent", "cc");
var ub = S("eventCategory", "ec"),
xb = S("eventAction", "ea"),
yb = S("eventLabel", "el"),
zb = S("eventValue", "ev"),
Bb = S("socialNetwork", "sn"),
Cb = S("socialAction", "sa"),
Db = S("socialTarget", "st"),
Eb = S("l1", "plt"),
Fb = S("l2", "pdt"),
Gb = S("l3", "dns"),
Hb = S("l4", "rrt"),
Ib = S("l5", "srt"),
Jb = S("l6", "tcp"),
Kb = S("l7", "dit"),
Lb = S("l8", "clt"),
Ve = S("l9", "_gst"),
We = S("l10", "_gbt"),
Xe = S("l11", "_cst"),
Ye = S("l12", "_cbt"),
Mb = S("timingCategory", "utc"),
Nb = S("timingVar", "utv"),
Ob = S("timingLabel", "utl"),
Pb = S("timingValue", "utt");
S("appName", "an");
S("appVersion", "av", "");
S("appId", "aid", "");
S("appInstallerId", "aiid", "");
S("exDescription", "exd");
S("exFatal", "exf");
var Nc = S("expId", "xid"),
Oc = S("expVar", "xvar"),
m = S("exp", "exp"),
Rc = S("_utma", "_utma"),
Sc = S("_utmz", "_utmz"),
Tc = S("_utmht", "_utmht"),
Ua = S("_hc", void 0, 0),
Xa = S("_ti", void 0, 0),
Wa = S("_to", void 0, 20);
This seems to be a registry that binds aliases, e.g. appName
→ an
.
Anyway, with that in mind, we can now start to unravel the minified mess of the above JSON request I sent.
request = {
"Query string": {
"apiVersion": "1",
"clientVersion": "j83", //
"adSenseId": "1398337363", // you guys use AdSense, huh?
"hitType": "pageview",
"_s": "1",
"location": "https://vuejs.org/",
"language": "en-us",
"encoding": "UTF-8",
"title": "Vue.js",
"screenColors": "24-bit",
"screenResolution": "2000x600",
"viewportSize": "1988x600",
"javaEnabled": "0",
"usage": "IEBAAAAB~", // this is really dodgy, it's random on every load.
"jid": "791792288",
"gjid": "834920789",
"clientId": "183173963.1595265292",
"trackingId": "UA-46852172-1",
"_gid": "1707634750.1595265292",
"_r": "1",
"z": "1439589530" // calculation of "Math.round(2147483647 * Math.random());", couldn't find the real name
}
}
Looking at all these statistics tells me one thing: using Google Analytics diminishes user privacy.
With that in mind, perhaps sending it to Google in the first place isn't really the wisest idea. I'd love to work something out, and see how we can replace it.
After reloading the page twice, and copying the data sent to the /collect
endpoint, I've used a JSON diffing tool to compare the two data sets sent.
There are a few unique identifiers here, which are also saved as cookies.
As a side note: are you aware that by loading a script that sets tracking cookies, you are supposed to ask for user consent? This is enforced under GDPR: you can find more about that here.
Currently, by using this product, you are sending the following data to Google:
- AdSense identifiers (no idea, either)
- URL of the page
- Title of the page
- Screen colour range
- Screen resolution
- Viewport size
- Java plugin status
- User agent
- PII, such as an IP address
- unique user identifiers
- client identifier
This easily allows Google to fingerprint and watch users travel the internet. They're probably watching you too, right now.
Plus, you said you're just using Google Analytics as a hit counter. Has it crossed your mind that perhaps most of this data is actually useless to you?
I'm guessing Netlify's Analytics system doesn't have this same problem? Neither do the FOSS solutions I listed.
Google Analytics is quite the slow down too, coming in at a whopping 1106 milliseconds (1.1 seconds) loading time. Just by looking in my developer utilities, it's clear to see that Google Analytics severely slows down the loading time of any and all vuejs.org pages.
This definitely impacts the SEO of vuejs.org.