Web Fonts and Privacy

About Web Fonts

Web fonts are fonts that are downloaded by the web browser when the web browser needs them.

I both love them and hate them, depending upon how they are used.

First, a bit of history.

Back in the “old” days when the World Wide Web was brand new, typography for the web simply did not exist. It was the web browser that decided what font was used with absolutely no input whatsoever from the web developer.

Netscape Navigator was the first innovator to change that, with the introduction of the <font> tag. A web developer could use that tag to specify what font they wanted used, and if it was available on the system, it would be used. The tag became part of HTML 3.2 and Microsoft released a free downloadable set of fonts called the “Core Web Fonts” that helped unify what fonts were available.

This is why Comic Sans MS was used so heavily in amateur web design, it was a font that didn’t look corporate and you knew there was a good possibility it was installed on the system of people viewing your web site.

As the web moved more and more away from an education and collaborative research platform to a commercial platform, fonts and capabilities of fonts became more and more important in web design. This resulted in some unfortunate side effects.

Browser Fingerprinting with Fonts

The current capabilities of fonts allow information about the user’s system to be leaked to the web server. I consider it to be an exploit. Anything that leaks more about the system than the user agent and platform is a security / privacy hole. The web page https://www.kirupa.com/html5/detect_whether_font_is_installed.htm describes one method of detecting installed fonts with JavaScript.

Since it is very rare for two systems to have identical fonts installed, font detection can be used as a so-called “super cookie” to identify users even when they have deleted their cookies.

Web Fonts are part of the solution to this problem.

Web Fonts as a Solution

If browsers would ship with a few built-in fonts and refuse to ever use system fonts, then JavaScript/Canvas tricks to detect system fonts would only report the fonts that shipped with the browser. This would not give away any more information than the user agent string already gives away. It might help a server determine if a user agent string is being spoofed, but that can usually already be determined with other JavaScript techniques.

FireFox for example could ship with their Fira fonts, and perhaps the Liberation Fonts and the IBM Plex fonts. If the web page requests those fonts, they would be used, with some substitutions (e.g. Liberation Sans could be used if the page asked for the Arial system font) but system fonts would never be used.

That would solve the problem of system font detection in browser fingerprinting, but it would also severely limit web design.

That’s where I see web fonts coming to play.

When a web designer wants a particular font used, the web designer specifies a web font. When that web font does not exactly match one of the fonts that shipped with the browser, the browser downloads it even if an exact match is installed on the system. That would completely neuter system font detection via JavaScript and fix what I consider to be a currently active exploit. Other browser fingerprinting methods exist, but font detection is a big one. The fewer the methods that exist, the less effective browser fingerprinting is.

The Problem of Web Fonts

Screenshot showing web font trackers

The problem with web fonts is that they are very rarely served from the same server as the web page. This makes them a third party resource, as I discussed on my Primer on Privacy and Tracking page. That itself is not bad, except it often means it becomes a direct source for third party tracking via cookies. That is what Google Fonts does, and that also is what many of the commercial web font servers do.

To resolve this issue, privacy conscientious webmasters will either host the fonts themselves, or use a third party font server that is committed to never using cookies.

Commercial Web Fonts

Most sources for commercial web fonts literally require that you use their font servers. You are paying them for the privilege of allowing them to track your users. Do not use them.

Good Design Needs Good Fonts
Affiliate Link

There is one exception – YouWorkForThem.com. Their license allows you to host the web fonts yourself, giving you the ability to have high quality commercial fonts without exposing your users to trackers.

If you have need for commercial web fonts, I really really really strongly urge you to buy them.

And yes, my link to them is an affiliate link. But even though I rarely buy fonts, I have been their customer for several years, and their font selection is really good.

With respect to hosting their fonts yourself, I wrote a font server that makes it easy. I will be sharing it later.

The Geeky Part – Defining Web Fonts

In order to use a web font, a CSS file has to define the font. This is done with a @font-face declaration:

@font-face {
  font-family: 'Example Font';
  font-style: normal;
  font-weight: 500;
  src: local('Example Font Medium'),
       url(https://fonts.example.org/ExampleFont-Medium.woff2) format('woff2'),
       url(https://fonts.example.org/ExampleFont-Medium.woff) format('woff');

The first three declarations: font-family, font-style, and font-weight are what the browser uses to determine if the font defined by the @font-face is a match for what the page design is requesting.


Technically this can be anything you want, but it is a good practice to use the actual typeface name. You can use a different name for the font, e.g. ‘Heading Font’ or whatever floats your boat. In your CSS for your web design, where you want this font to be used, you must use the same name you defined here.


There are only three valid values for this field: normal, italic, or oblique. Most fonts have either italic or oblique and not both, so I personally define the style as italic even when the actual font file is technically oblique. There is a technical difference, but the technical difference does not seem to matter, so I just use italic when the font is not normal (upright roman).


There are nine standard weights. Only normal (400) and bold (700) can be defined by name, so I prefer to just always define them by number:

Font Weight Names and Corresponding Integer Values
Name Number Name Number
Thin 100 SemiBold 600
ExtraLight 200 Bold 700
Light 300 ExtraBold 800
Normal 400 Book / Black 900
Medium 500

Most font families will only have 400 or 400 and 700 weights available, but some have more.


When the previous three parameters match what the CSS for the page design call for, the src definition tells the browser where to find the actual font to use.

Each defined place to look is delimited by a comma, and the browser goes through the list left to right until it finds a match it can load.

In the example @font-face definition, there are two local() definitions first. Those are optional but they tell the browser names that the font might be installed as on the system itself, allowing the browser to use the specified font if the user has it locally installed.

After the two local() definitions are the url() definitions. These tell the browser where it can download a suitable font to use. First a WOFF2 font is defined, it is defined first because it is a smaller file size for the browser to download. The WOFF2 format has very wide support. The site https://caniuse.com/#feat=woff2 reports that it works with every currently supported major browser except for Internet Explorer 11, Safari on El Capitan and older, Opera Mini, and UC Browser.

Opera Mini does not support web fonts at all. Opera does, but the Mini version is designed to explicitly be a very low bandwidth consuming browser.

For the other three browsers that do not work with WOFF2, https://caniuse.com/#feat=woff reports they do work with WOFF so if it is important to us that the correct font is available to those systems, we can add the second url() definition pointing to the WOFF version. Browsers that do not support WOFF2 will see that the first defined url() is WOFF2 from the format() declaration, skip it, and fetch the WOFF version instead.

Important Notes

In addition to WOFF2 and WOFF, two other font formats exist that can be used as web fonts: EOT and TTF. EOT is a proprietary format developed by Microsoft in the early days of web fonts, it only works with Internet Explorer and should not be used. It is a larger download than WOFF and all recent versions of Internet Explorer support WOFF. Having the EOT version is a waste of space. TTF is really large and never was intended to be a web font, every current browser that can make use of TTF can also make use of WOFF and frequently WOFF2 as well. TTF is fine for a system font, but it should not be used for a web font.

Note that just because you have defined a web font does not guarantee it will be used. Some users disable the loading of web fonts, and if the web font server uses cookies, privacy software will often block it. So it is important to define a suitable alternative in your web site design.

With the “Example Font” defined above, if it was the Sans Serif font I wanted to use in the context of the HTML <caption> element, I might ask for it in my web page CSS like this:

caption {
  font-family: "Example Font", Helvetica, Arial, sans-serif;
  font-style: normal;
  font-weight: 500;

When the browser comes across a <caption> element it will look for a defined @font-face with the declared family, style, and weight that matches. If it can either find a suitable local font from that definition or a font it can download from that definition, everything it cool. But if the user does not have the font installed locally and has disabled web font downloads, then the browser will look for Helvetica followed by Arial and finally default to the browser defined default sans-serif font.

Personally I would leave the Helvetica and Arial declarations out. They usually do not hurt, but if the web font I have defined is not going to be used, letting the browser defined sans-serif default be used is fine for me. Some people with reading issues, like dyslexia, may have disabled web fonts specifically because they have defined a default (like Adys font) they want used that is easier for them to read.

Unicode Range Optimization

When defining the @font-face there is another parameter that can be defined: the unicode-range parameter.

Some fonts are a rather large download because they contain character support for many different language character ranges. If a web page only contains Latin glyphs, should you force the user to download a large font that also contains Cyrillic and Greek glyphs?

You can optimizing the font by breaking it up into several different font files that each cover a different Unicode Range, and make different @font-face declarations that are identical except for the file specified and the Unicode Range specified. The browser will then look at the characters that are needed and only download the file that has the range of characters it actually needs.

I personally do not bother with this, even the fattest font files are generally small downloads and when the font server is set up correctly, the browser will cache them once downloaded. Also with commercial web fonts, the font license often explicitly states you are not allowed to create derivatives. Breaking the font file into subsets may be considered creating a derivative.

Self Hosted Font Server

To use web fonts, you need a web server set up to properly server them. Fonts have to be served with a particular header telling the client it is okay to use them. Honestly I think that system is a bit broken, the client should not be what decides if it is allowed to use the font, the server should simply refuse to serve the font if it does not want the client to use it, and that is easy to do. But the standards committee came up with the system that we have to use so we have to use it.

To correctly serve fonts from Apache, add the following to your .htaccess file to make sure they are sent with the correct MIME type, header telling clients they can use it, and a decent cache time:

AddType font/woff .woff
AddType font/woff2 .woff2
<FilesMatch "\.(woff|woff2)$">
Header set Cache-Control "max-age=7257600"
Header set Access-Control-Allow-Origin "*"

The AddType declarations make sure the font is served with the correct MIME type. Most Apache configurations are already set up by default to correctly serve WOFF as font/woff but some are not. However, given that WOFF2 is a newer format, quite a few are not correctly set up to serve WOFF2 as font/woff2 by default.

The Header set Cache-Control "max-age=7257600" tells the browser it can cache the file for up to twelve weeks. My personal experience is that browsers will check after a few days to see if the file has changed, but they might not. Browsers will delete it from the cache when it is twelve weeks old if they have not deleted it already. Since font files rarely change, using a long cache time is preferred.

The Header set Access-Control-Allow-Origin "*" is what tells the browser it is allowed to use the font. The * is a wild card saying the font can be used regardless of the domain the web page that wants it is from, but the intent of the specification was to bloat the HTTP headers by having a list of what domains you want to allow the font to be used with.

Almost no one actually does that, not even commercial font servers that want tight control. Alternative methods are used.

The best way to restrict access is to simply use the Apache mod_rewrite module to refuse to serve the font file if the HTTP Referrer is not in a white list of domains allowed to use the font.

If you are going to be using more than just a few web fonts, for self hosting you probably want to set up a dedicated font server.

The FlossWoff2 project I have at github will work just fine for that. You can even use it for commercial fonts, just make sure the license allows you to self host.

Third Party Font Server

Most people end up using a third party font server. That actually has a lot of benefits, in addition to you not needing to be the one who sets up the @font-face definitions.

With a third party font server, the font may already be in the user’s cache when they visit your site, even if it is their very first visit to your site. That provides a smoother experience for the user.

What browsers actually do, they do not download the font just because there is an @font-face definition. When an @font-face declaration matches what the page calls for, if the browser does not have a system font that matches or a cached version of the remote font, they initially render the page using system fonts and then download the font. Once the font is downloaded, they re-render the text. This can cause the page to jump as the text changes. When the font is cached, that does not happen.

The drawback to third party font servers, the vast majority of them use cookies with unique identifiers that can be (and often are) used to track your users and build profiles about their browsing habits. These profiles are often tied to real identities and then sold. Your users become a product without consent. That is morally reprehensible.

If using a third party font server, please make sure it is one that does not track.

The service I am starting is the only one I am aware of that is dedicated to being tracker free and does not ever use cookies. More information on that is to come.

Leave a Reply

Your email address will not be published. Required fields are marked *

Anonymity protected with AWM Pluggable Unplugged