see all posts

Enforcing Do Not Track with Content Security Policy

Using Content Security Policy to enforce Do Not Track

With new scandals being reported all the time, privacy is becoming more and more important to users, and as a website administrator, it's important to treat your users' data with care. A powerful way to treat visitors' privacy with respect is by enforcing Do Not Track.

Do Not Track

Do Not Track (DNT) is a browser setting which can be used to tell websites and third-party scripts (advertisers, analytics, social media, etc) that the user doesn't want to have their activity tracked across websites. It can be enabled for any modern browser.

DNT works by sending a header with each request. If DNT has been enabled, the dnt header will have the value 1. DNT isn't enforced by the browser, so it's up to the website administrator to implement it.

However, it's important to note that DNT isn't enforced by your browser. The header is sent as a request to the host server, and may or may not be respected, depending on whether the website implements it or not.

Implementing Do Not Track

Typically, users who enable DNT will want to browse anonymously. The EFF's DNT policy includes exceptions which allow for a site to function normally and in a secure manner. This seems reasonable for most sites, so in general, DNT shouldn't interfere with any expected site functionality, such as logins or purchasing.

Ideally, DNT would prevent analytics scripts, ads, and any third-party scripts which might track the user from running. Since it's impossible to know what third-party servers log when resources are requested from them, if third-party scripts are still requested when DNT is enabled, the website is "passing the buck", in a sense, and hoping that the third-parties will enforce DNT.

With that being said, if DNT is enabled, the best thing to do is to disallow all third-party scripts.

How I enforce Do Not Track

As an example, this site uses Google Analytics for traffic analysis and Disqus for comments.

While Google Analytics doesn't expose any individual's data to the website administrator, Google is very likely to be familiar with your browsing habits. Disqus is a great tool for comments, and is wide-spread and easy to use, but it collects a certain amount of data as well.

So, starting yesterday, I've disabled Google Analytics and Disqus comments for anyone who enables DNT. In the interest of privacy, I've also self-hosted any other resources I would have typically pulled from a CDN (Bootstrap CSS, Google Fonts, etc).

For users who have enabled DNT, I'm enforcing it in two ways:

  1. I prevent rendering of any third-party scripts
  2. I remove those third-party domains from my Content Security Policy

Typically, preventing any third-party scripts from even rendering to the response HTML would be sufficient, but I wanted to take it a step further and enforce DNT on my Content Security Policy. This way I can be confident that nothing will accidentally slip through the cracks.

Enforcing Do Not Track with Content Security Policy

Content Security Policy (CSP) is a powerful way to control what resources are allowed or disallowed on your pages. Some examples include only allowing scripts to be rendered from specific domains, or preventing unsafe inline JavaScript execution. The CSP header is set on the server and sent back with the response to the client. It is then interpreted and enforced by the user's browser.

For example, if DNT is disabled, the script-src part of my CSP looks like this (majority omitted for brevity):

content-security-policy: ... script-src script-src 'self' ...;

This allows the browser to request scripts from the origin it is currently on ('self') as well as the other origins specified.

When DNT is enabled, the same part of my CSP header looks like this:

content-security-policy: ... script-src 'self';

This only allows the browser to load scripts from my site, and prevent loading from any other origin.

In addition to restricting script-src, I've also enabled similar protection for other parts of my CSP, including style-src, img-src, and frame-src. If any resources slip in which make requests to any exteral sites, the request will fail and you'll see an error in your JavaScript console. This way, I can be confident that my site fully respects Do Not Track.


One drawback to utilizing the CSP to enforce DNT is that it disallows dynamically allowing certain resources, like how Medium enforces DNT on embeds. This may be something I investigate in the future, as the potential for users to provide consent to a DNT violation is clearly superior than forcing them to disable the setting.

Another drawback is that implementing DNT in both places (CSP and HTML) requires duplicated effort. It's simpler and less error-prone to manage DNT in only one location. This is an experiment and may change in the future.

More information

For more information, the Electronic Frontier Foundation has a great guide on How to Implement DNT. I recommend skimming through it to get a better idea of some implementation details.