Static File Caching with Craft CMS 3, aka The Best Of Both Worlds

21 November 2018

In a recent redesign of the PutY­our­Light­sOn web­site, we decided to start with a blank slate. It became an excel­lent oppor­tun­ity to re-eval­u­ate the state of web pub­lish­ing tools in 2018 with the aim of util­ising the best pos­sible means to build a light­ning-fast site that would still enable a great author­ing experience.

Static file caching

It is no secret that I love work­ing with Craft CMS and it of course was my first choice in terms of author­ing exper­i­ence. Yet I am also well aware that sites powered by Craft can some­times feel a bit slug­gish if set up inef­fi­ciently. So the first option was to build an optim­ised and light­weight Craft site to get the most per­form­ance out of it.

Option 1 #

Build an optim­ised and light­weight Craft site to get the most per­form­ance out of it.

Google’s PageSpeed Insights trig­gers an Improve Serv­er Response Time” alert if your serv­er response time is above 200 mil­li­seconds. TTFB (time to first byte) is the time it takes for the web serv­er to respond with the first byte of data to a request for a web page and is an import­ant met­ric in web performance.

When a web serv­er receives a request for a web page, one of two things gen­er­ally hap­pens. The first is that the serv­er returns data or a file from sys­tem memory or from the file sys­tem, for example when a request comes in for a page that has been pre­vi­ously pro­cessed and stored in memory, or for an image or anoth­er resource file. The second is that the request is passed on to the web serv­er to pro­cess and do whatever it needs to do before return­ing a result.

Craft CMS is writ­ten in PHP, a pro­gram­ming lan­guage that gen­er­ally runs on an Apache or Nginx web serv­er. When the web serv­er receives a request for a page that is man­aged by Craft, a PHP exe­cu­tion thread handles the boot­strap­ping of the web app and a chain of events is star­ted in order to determ­ine how exactly the request should be pro­cessed. At the end of that chain of events, a response is sent back to the user’s web browser, and the time it takes for the first byte to com­plete that round trip is the TTFB.

Obvi­ously a web page may need to do a bunch of pro­cessing to determ­ine what to return to the web browser, so some­times that trip to the Craft applic­a­tion is unavoid­able and poten­tially slow (upwards of 600 ms). But it makes very little sense to pro­cess every request to the same page over and over again, espe­cially if the res­ult is always going to be the same.

Ima­gine you worked at a bakery that sold deli­cious home-made scones, the best in town. On a busy day, you run out of scones and have to tell each cus­tom­er that comes in that unfor­tu­nately there are none left. You’d like to bake some more but deal­ing with all the cus­tom­ers is con­sum­ing all of your time, so you put up a sign on the out­side of the bakery: Sold out of scones”. That frees you up to bake some more and when the new batch of scones is ready you simply change the sign: Freshly baked scones!”.

The ana­logy above is sim­il­ar to a web serv­er fir­ing up a PHP pro­cess and run­ning Craft on each and every request to a page that determ­ines wheth­er there are scones or not. That pro­cess may be a very involved one, but once it is determ­ined it does not need to be repro­cessed until some­thing has changed with­in the sys­tem, i.e. the scones have sold out or a fresh batch has come out of the oven.

Now ima­gine an about” page on your web­site that out­puts some text about your com­pany. Even though that’s all it does, the web serv­er will still by default pro­cess each and every request that comes in for that page and always return the same res­ult. One simple and effect­ive way to avoid this is to use stat­ic file cach­ing. Stat­ic­ally cach­ing a web page means stor­ing the res­ult of the request some­where and return­ing that cached data to any request that comes in, without doing any pro­cessing. This res­ults in huge per­form­ance gains — responses are sent almost imme­di­ately and with little or no pro­cessing power involved.

So the second option was to use a serv­er-side stat­ic file cach­ing tool such as Var­nish for Apache or Fast­CGI for Nginx to speed up response times and increase poten­tial requests per second.

Option 2 #

Use a serv­er-side stat­ic file cach­ing tool such as Var­nish for Apache or Fast­CGI for Nginx to speed up response times and increase pos­sible requests per second.

When I looked into the details of set­ting up serv­er-side stat­ic file cach­ing, I was sur­prised to find how much setup is required (this art­icle provides an excel­lent, in-depth guide). While not overly com­plex, it was quite a bit more involved than I had expec­ted and the thought of hav­ing to do this for mul­tiple sites hos­ted on mul­tiple serv­ers was off-put­ting. Test­ing loc­ally and on sta­ging serv­ers also meant poten­tially repeat­ing the pro­cess mul­tiple times per site.

The oth­er red flag was cache inval­id­a­tion. When any con­tent changes in your CMS, you ideally only want the affected cached pages to be inval­id­ated (also known as cleared or bus­ted), how­ever the gen­er­ally used approach is to inval­id­ate the entire cache. That means that mak­ing a change to any field on the about” page would res­ult in an entire website’s cache — poten­tially hun­dreds or thou­sands of pages — being inval­id­ated because of a change to only a single page.

There must be a sim­pler and bet­ter way to do this”, I thought, and set out to build a Craft plu­gin that enables stat­ic file cach­ing with min­im­al setup and with intel­li­gent cache invalidation.

Option 3 (the win­ner) #

Build a Craft plu­gin that enables stat­ic file cach­ing with min­im­al setup and with intel­li­gent cache invalidation.

In the early plan­ning stages I was reminded of a well-known quote that is attrib­uted to Phil Karlton:

There are only two hard things in Com­puter Sci­ence: cache inval­id­a­tion and nam­ing things.

Nam­ing the thing turned out to be the easi­er of the two. Blitz”, mean­ing light­ning in Ger­man, was a trib­ute to the offi­cial Craft CMS con­fer­ence, Dot­All, com­ing to Ber­lin in 2018. Cache inval­id­a­tion, how­ever, was a much deep­er rab­bit hole than I had expec­ted with lots of les­sons learned along the way.

Today, installing and set­ting up the Blitz plu­gin takes less than a minute and one-click updates are avail­able through the offi­cial Craft plu­gin store. The per­form­ance gains are com­par­able to serv­er-side stat­ic file cach­ing with none of the headache.

As noted above, cache inval­id­a­tion, done right, is hard! Yet the Blitz plu­gin has an unfair advant­age over the serv­er-side stat­ic file cach­ing options in that it is intim­ately con­nec­ted with the CMS. It uses nat­ive events in Craft to determ­ine exactly which pages load which ele­ments (entries, cat­egor­ies, users, etc.). It even goes a step fur­ther and determ­ines which pages could pos­sibly load which ele­ments, so for example a page that loads the 10 most recently pub­lished art­icles will be inval­id­ated if a com­pletely new art­icle is published.

Cache warm­ing is anoth­er tech­nique used by Blitz. This means that each time a cached page is inval­id­ated, Blitz will auto­mat­ic­ally regen­er­ate the cache with the latest changes, mean­ing that there will nev­er be a case where some unlucky user lands on an inval­id­ated (and hence uncached) page and has to wait sev­er­al seconds for it to load.

The Blitz plu­gin has become a main­stay plu­gin on all the sites we build. It sup­ports multi-site setups in Craft and often res­ults in a TTFB of 100 ms or less, which makes a site feel extremely zippy and is sure to sat­is­fy Google. It’s hard to ima­gine going back to not using some meth­od of stat­ic file cach­ing and Blitz truly enables light­ning fast sites with a great author­ing exper­i­ence — the best of both worlds.

Blitz is avail­able in the Craft Plu­gin Store and comes with a free tri­al so you can see just how much of a speed and per­form­ance boost it can give your site.