mincss in action - sample report from the wild

22 January 2013   8 comments   Python, Web development

Powered by Fusion×

First of all, to find out what mincss is read this blog post which explains what the heck this new Python tool is.

My personal website is an ideal candidate for using mincss because it uses an un-customized Bootstrap CSS which weighs over 80Kb (minified) and on every page hit, the rendered HTML is served directly from memcache so dynamic slowness is not a problem. With that, what I can do is run mincss just before the rendered (from Django) output HTML is stored in memcache. Also, what I can do is take ALL inline style blocks and all link tags and combine them into one big inline style block. That means that I can reduce any additional HTTP connections needed down to zero! Remember, "Minimize HTTP Requests" is the number one web performance optimization rule.

To get a preview of that, compare http://www.peterbe.com/about with http://www.peterbe.com/about3. Visually no difference. But view the source :)

Document size: Before

Document size: After

Voila! One HTTP request less and 74Kb less!

Now, as if that wasn't good enough, let's now take into account that the browser won't start rendering the page until the HTML and ALL CSS is "downloaded" and parsed. Without further ado, let's look at how much faster this is now:

Waterfall view: Before

Waterfall view: After

How cool is that! The "Start Render" event is fired after 0.4 seconds instead of 2 seconds!

Note how the "Content Download" isn't really changing. That's because no matter what the CSS is, there's still a tonne of images yet to download.

That example page is interesting too because it contains a piece of Javascript that is fired on the window.onload that creates little permalink links into the document and the CSS it needs is protected thanks to the /* no mincss */ trick as you can see here.

The code that actually implements mincss here is still very rough and is going to need some more polishing up until I publish it further.

Anyway, I'm really pleased with the results. I'm going to tune the implementation a bit further and eventually apply this to all pages here on my blog. Yes, I understand that the CSS, if implemented as a link, can be reused thanks to the browser's cache but visitors of my site rarely check out more than one page. In fact, the number of "pages per visit" on my blog is 1.17 according to Google Analytics. Even if this number was bigger I still think it would be a significant web performance boost.


Steve Souders points out a flaw in the test. See his full comment below. Basically, what appears to happen in the first report, IE8 downlads the file c98c3dfc8525.css twice even though it returns as a 200 the first time. No wonder that delays the "Start Render" time.

So, I re-ran the test with Firefox instead (still from the US East coast):

WebpageTest before (Firefox)

WebpageTest after (Firefox)

That still shows a performance boost from 1.4 seconds down to 0.6 seconds when run using Firefox.

Perhaps it's a bug in Webpagetest or perhaps it's simply how IE8 works. In a sense it "simulates" the advantages of reducing the dependency on extra HTTP requests.


Rene Dudfield
Very cool! <3 it :)

There'd be a much bigger audience for it if it came as a script, rather than tied up in Django stuff. Well, at least then I could use it ;)

Have you tried it on other frameworks, or websites apart from bootstrap ones?
Peter Bengtsson
mincss has nothing to do with Django. Did you see this? http://www.peterbe.com/plog/mincss

I have, for example, not used it on my one-page-tonnes-of-javascript *app* http://aroundtheworldgame.com because it's just too much Javascript dependent.
Rene Dudfield
Aha! No, I missed that link. Brilliant :)
Joe Ciskey
The obvious feature to add is taking a list of pages, applying the script to each of them, and then aggregating the entire result so that you have a site-wide CSS file that can be cached by browsers. I might just fork this and give it a try.
Peter Bengtsson
It already exists. The API is built such that you feed it URLs till your done then call the `.process()` method to start the calculation. For example:

  >>> from mincss.processor import Processor
  >>> p = Processor()
  >>> p.process_url('http://example.com/page1.html')
  >>> p.process_url('http://example.com/page2.html')
  >>> p.process()

However, if you do that, the `p.inlines` will be confusing since you won't know which URL it came from. There's a shortcut to when you just have 1 single URL to worry about and that's to use `.process()` directly:

  >>> from mincss.processor import Processor
  >>> p = Processor()
  >>> p.process('http://example.com/page.html')

Or you can actually do this too:

  >>> from mincss.processor import Processor
  >>> p = Processor()
  >>> p.process('http://example.com/page1.html', 'http://example.com/page2.html')

Messing around with doing one page at a time and using inline is more advanced and perhaps something just for those who want the absolutely fastest.
Steve Souders
Hi, Peter. It's fun to read about how mincss is making sites faster. I have one issue with your results, however. You mention how the page starts rendering much sooner. If we look at the "before" results ( http://www.webpagetest.org/result/130121_H_1MQ/1/details/ ) we see that rendering is blocked waiting for c98c3dfc8525.css. The weird thing is we can see that c98c3dfc8525.css is downloaded TWICE (see requests #2 & #12). It would be great if you could figure out why this happened (IE8 anomaly?) and re-run to get more comparable results.
Peter Bengtsson
It appears to respond with a 200 the first time so I honestly don't know what's going on.

I'll re-run the tests with a sane browser and see if it's reproduce-able.
Peter Bengtsson
Updated the blog post.

Your email will never ever be published

Related posts

mincss "Clears the junk out of your CSS" 21 January 2013
mincss now support PhantomJS 02 February 2013
Related by keywords:
Fastest way to uniqify a list in Python 14 August 2006
mincss "Clears the junk out of your CSS" 21 January 2013
Gzip rules the world of optimization, often 09 August 2014
premailer.py - Transform CSS into line style attributes with lxml.html 11 July 2009
The awesomest way possible to serve your static stuff in Django with Nginx 24 March 2010
Optimization of getting random rows out of a PostgreSQL in Django 23 February 2011
Fastest way to thousands-commafy large numbers in Python/PyPy 13 October 2012
premailer now excludes pseudo selectors by default 27 May 2013
Optimizing MozTrap 04 June 2014
This site is now 100% inline CSS and no bytes are wasted 05 March 2013
XHTML, HTML and CSS compressor 07 April 2004
mincss version 0.8 is much much faster 27 February 2013