Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
212 lines (186 sloc) 10.3 KB
title author layout categories tags
Web Font Anti-pattern: Data URIs
Zach Leatherman

After I posted my Critical Web Fonts article on Twitter, I had an interesting conversation with a developer named Wim Leers about embedding Web Fonts as Data URIs.

@zachleat @MarcDrummond Why not just embed as a data URI in the critical CSS? Cached, no FOUT. Example:

— Wim Leers (@wimleers) January 6, 2016

His suggestion was to embed the font directly in a style block on the server rendered markup, something like:

<!doctype html>
<html lang="en">
	<meta charset="utf-8">
	@font-face {
		font-family: Open Sans;
		src: url("data:font/woff;charset=utf-8;base64,…") format("woff");
		font-weight: 400;
		font-style: normal;

This approach should not to be confused with the asynchronous loadCSS Data URI approach documented (but deprecated) on the Filament Group blog.

I’ve seen a similar variant of this Data URI approach used by, although their approach used an external stylesheet rather than an inline style element. I talked a little bit about it at Velocity last year.

I consider this approach to be an anti-pattern for normal font loading scenarios for a few reasons:

  1. It puts a large Data URI in the critical path. Remember that CSS blocks rendering. The goal here is to avoid a Flash of Invisible Text (FOIT) and minimize our Flash of Unstyled Text (FOUT). It obviously isn’t a good tradeoff to delay the entire page render to avoid FOIT and FOUT. Since 42% of web sites load more than 40KB of web font page weight, many sites would need to put 40KB of Data URIs in their critical path, far exceeding the recommended 14KB window for critical content.
  2. The font format you embed is probably not optimal. If you embed a Data URI, you’ll probably embed the WOFF format to give you ubiquity (better browser support) even though the WOFF2 format usually has about a 30% smaller footprint. Embedding a single format removes the benefit of automatic format selection that a typical comma separated src attribute provides. You aren’t required to list only one src here, but for example let’s say you embed a WOFF2 format Data URI and list the WOFF format as an alternate external url in the src attribute. There are still quite a few modern browsers that don’t support WOFF2 and they would load that big Data URI and still have to resort to using a fallback format URL. (See Appendix 1, Data URI and Fallback src below.)
  3. Ability to cache fonts suffers. This approach worsens with repeat views because the Data URI is tightly coupled to the markup and will not be cached (unless the user visits the same destination twice).
  4. The other drawback Bram Stein mentions in his latest presentation (and has a great waterfall showing it, too): if you have multiple web fonts, making them all Data URIs forces them to be loaded sequentially (bad) instead of in parallel (good).

Update: Wim Leers has since informed me that the approach he was proposing was not inlined critical CSS, but rather a blocking CSS stylesheet, a la the approach used by Alibaba and Medium. The above drawbacks still stand, save for #3. What’s more, this approach probably exacerbates drawback #1, given the performance gains we already know exist when using Critical CSS.

For those reasons, this method is considered to be an anti-pattern and should not be utilized on a production site. It may seem superficially beneficial, but it’s actually bad for performance.

But just for the sake of argument, let’s put it into action and see how it affects the fonts on my web site:

(Times generated using Chrome Canary’s Developer Tools Network Throttling in Regular 3G mode)

Default Font Loading Roman Data URI and Italic Data URI and Bold Data URI and Bold Italic Data URI
Initial Render 573ms
Roman Loaded 2.12s 1.01s
Italic Loaded 2.12s 2.05s
Bold Loaded 2.20s 2.11s
Bold Italic Loaded N/A N/A N/A N/A 2.38s

Interestingly enough, if you only inline the Roman version and you’re willing to sacrifice almost 400ms of extra time for initial render (wow, that is a big sacrifice), you can cut a whole second off the Roman web font rendering time. Initial render suffers even worse with a second inlined font, and if you inline more than two fonts (ignoring web font SPOF concerns) performance-wise you’re better off doing nothing (compare the 1st and 4th columns). Also note that I didn’t test repeat views in the above table because the data was conclusively negative without it.

But wait…

The discussion started with embedding the entire web font, but what if we apply this idea to the Critical FOFT approach? What if we only inline the critical subset font? My hunch is that the subset 11KB WOFF font file (much smaller than the 40KB average) is probably still too large to put into the critical path, but let’s test it out.

(Times generated using Chrome Canary’s Developer Tools Network Throttling in Regular 3G mode)

Update March 17, 2016: Per a discussion with @pixelambacht, we were able to optimize the WOFF font file to only 5KB! I’ve updated the results below. This shaved about 5-10 more milliseconds off in the tests that I ran, so depending on your design preferences you should make your own choices about whether those optimizations are worth it.

Empty Cache Visit
Critical FOFT Critical Roman Data URI
Initial Render 570ms
Stage 1 Render
(Critical Roman)
967ms 580ms
Stage 2 Render
(Roman, Italic, Bold, Bold Italic)
2.70s 2.42s

Wow! No visible FOUT! This is a huge deal. The critical web font is available on first render with an empty cache on 3G. This is great! The only problem here, of course, is that for repeat views the Data URI is still inlined on the page. Let’s test that out:

Repeat Views
Critical FOFT Critical Roman Data URI
Initial Render 309ms 293ms
Critical Roman Loaded 479ms 435ms
Roman Loaded 479ms 435ms
Italic Loaded 479ms 435ms
Bold Loaded 479ms 435ms
Bold Italic Loaded 479ms 435ms

Times look marginally better here too. Huh. I think I’m gonna roll with this approach on my website and see how it plays out live. Thanks for the discussion Wim! I guess I learned here that just because something is an anti-pattern doesn’t mean you should throw the baby out with the bath water. You might get some benefit from using a piece of the approach. Data URI Critical FOFT!

Appendix 1, Data URI and Fallback src

@font-face {
	/* In many browsers it loads the giant Data URI but isn’t able to use it */
	src: url("data:font/woff2;charset=utf-8;base64,…") format("woff2"), url( /path/to/webfont.woff ) format( "woff" );

Retweet to share this post

★ Web Font Anti-pattern: Data URIs: (or is it??)

— Zach Leatherman (@zachleat) March 14, 2016
You can’t perform that action at this time.