How to prevent third-party scripts from collecting data, sending analytics, and more without user consent.
Disclaimer: This article is about blocking scripts that are known to be safe, like analytics — meaning scripts from third party services that you include on your own. To block malicious unwanted third-party scripts or prevent Cross Site Scripting attacks you should always consider using other solutions, like adding a Content-Security-Policy header!
Here at Snips we take privacy very seriously. Now that the GDPR is in effect, we have decided that our owned websites should never collect analytics on visiting users — unless they opt-in voluntarily.
Unfortunately, blocking third-party analytics can be a bit tricky. The problem is that these services are built to start immediately, and that they rely on small minified code snippets and script tags. You can try to modify the minified code yourself, but it gets a bit messy and hard to maintain, especially if you use several libraries.
So in order to handle this better — automatically — we created a small open source library called Yett (interesting meaning) that takes care of blocking the execution of analytics scripts. The library also allows you to later unblock these scripts, once a user has opted in.
To block scripts automatically might seem trivial — but it’s actually not that easy to block inline script tags!
So while digging into the subject, we found some technical subtleties that we thought would be nice to share with the community 😉.
Here are the requirements:
- The code must be loaded and executed synchronously, before any other script;
- At this point you have no clue how the final html will look, since the document has not yet been fully loaded;
- The blocked scripts must not be altered in any way (the goal is to prevent them from executing without touching their own code);
- We should be able to (eventually) unblock the scripts programmatically later on.
Below we list a few techniques that will actually work.
Groundwork — Using a MutationObserver to observe script elements insertion
MutationObserver comes in handy in this case, since you can register the observer on the document element ahead of time and be notified whenever DOM nodes get inserted, especially script nodes.
This alone won’t be sufficient of course, but it will be the first step towards our goal.
<script> tag from executing
Changing the script type attribute
type attribute to something other than
In the following snippets we arbitrarily picked
type you want.
Note that the script will still be downloaded.
Unfortunately, this won’t work for
The beforescriptexecute event is marked as deprecated, but is still supported by Firefox as of version 4.
Unfortunately, even if this is considered bad practice we don’t have much choice other than to call it.
The good news is that it works perfectly 😄.
Bonus - mark script tags with a custom type manually
Adding this extra attribute also allows the rest of the code to unblock these scripts automatically later on, and most importantly, prevents the script from downloading on
Preventing the execution of dynamically inserted script tags
As you certainly know, scripts can also be created and appended to the DOM programmatically. The MutationObserver will catch these, but changing the type or adding the events will not work in these cases.
Fortunately, there are solutions.
If we cannot prevent the execution after these scripts have been inserted, we can still return a slightly modified script instance whenever the script elements are created.
Adding ‘src’ and ‘type’ getters & setters on HTMLScriptElement
The last step is to ensure that the type is set to
Re-enabling these scripts afterwards
Of course, after blocking these scripts you may want to run them later on — for instance, when a user has voluntarily consented to opt-in.
The process is not detailed here, but the general idea is the following:
- Backup every script node the MutationObserver catches having type
- Then, expose a function that puts back those nodes into the
<head>when called with the
- Realistically, you would display a banner on your website with a button that the user can click to opt-in. Just call the function on click to un-block all the scripts.
For more details on the implementation, you can check out the Yett repository.
Wrapping it up
As you can see, it is absolutely feasible from a developer standpoint to prevent analytics from gathering data that should stay private — even if you don’t have direct access to the third-party code.
If you want to follow in our steps and truly respect the privacy of users browsing your websites, we made Yett, an open source library hosted on Github using all the code above in a convenient way. Collaboration and feedback are more than welcome!
I humbly hope that some of the tricks described here were useful to you, and thanks for reading this article! 👏
Thanks to those who reviewed this article, especially Jonas Ohlsson Aden for providing me with the idea and the opportunity to realize it!
If you liked this article and want to support Snips, please share it!
Follow us on Twitter 👉 snips.
If you want to work on AI + Privacy, check out out jobs page.