Performance optimization in Craft CMS

Performance is an important part of any website. We take a look at performance optimization tools and techniques as they apply to a website built in Craft CMS.

Whenever I get a spare moment I’m reading web development articles or researching tools to make my job easier. Recently I found an amazing tool for performance optimization that’s pretty amazing, Google PageSpeed Insights.

Google PageSpeed Insights will analyzing your site’s performance on mobile and desktop environments and then gives recommendations on how to optimize the performance of your webpages. Running it on Anecka gave some me some shocking news.

68/100? That’s not good. How is it on mobile?

ACK! At this point my stomach sank, mobile traffic makes up anywhere from 30-40% of our audience.

Still though, how bad could the performance really be? I mean we’re a relatively small site with not a lot of content. Even though we’re not using compression or browser caching effectively the actual response times can’t be terrible right?

While PageSpeed gave me a clue that my site wasn’t as optimized as it could be, it still didn’t tell me exactly how bad my load times actually were. Fortunately a quick round of research brought me to this tool: WebPageTest.org

“Well this seems legit”, I thought to myself. I typed in anecka.com and clicked “Start Test”, expecting my browser to crash from all of the spam and virus popups that would surely come flooding my way.

But wait! Instead it actually did something amazing. It gave me a fantastic report of exactly how much Anecka.com was sucking eggs in performance.

That’s a lot of “F’s.” I was a straight A student from middle school through college. I think I started breaking out in hives at this point. It got only worse as I checked out the portfolio page.

7.8 seconds! What’s worse, according to the “Start Render” column the user doesn’t even see anything on the page until 6.7 seconds. If they see the page at all, in all likelihood they’re gone at that point.

I started breaking out into a cold sweat. I could see the ghost of Alan Turning, Edsger Dijkstra, and what’s worse my mom staring down on me disappointedly “Surely you can do better than that dear boy.”

Darn right I can, I’m an engineer.

Dunking my head in a bucket of cold water strengthen by mind and stiff two finger pour of Bulliet Rye strengthened my resolve. I did what my experience messing with these infernal machines for over a decade has taught me to do, look without bias at hard data and find out exactly the stupid thing I did or didn’t do.

The first thing I noticed was the graph showing “Time to First Byte” on the portfolio page. It was contributing 4.6 seconds to the overall time, and nothing else was loading until it finished.

I suspected most of this was server-side processing by Craft. Fair enough, time to dive into Craft’s caching tags.

Caching in Craft

It was pretty easy to figure out, put {%cache%} {%endcache%} around dynamic content that won’t change very much. Fortunately our company’s site is pretty static, so I threw cache around almost everything dynamic. If we ever needed to update the site, it would be a simple task to flush the cache to make the changes appear live on the site.

While I didn’t dive into all the options you can use with {%cache%} , I did take advantage of {%cache globally%} for the nav:

{% cache globally %} <ul class="nav navbar-nav"> {% set menu = craft.entries.section("navigation").level('<=2') %} {% nav link in menu %} <li > {% ifchildren %} <ul > {% children %} </ul> {% endifchildren %} </li> {% endnav %} </ul><!-- END UL NAV --> {% endcache %}

Caching navigation globally is smart, because it’s going to be used on every page. If you didn’t cache the navigation globally, Craft would actually generate a separate cached copy for every URL in your site, which is pretty inefficient. With the “globally” parameter, Craft only generates and stores the navigation once.

So after (nearly) caching all the things how did our portfolio page do? After hitting Anecka.com/portfolio a few times to make sure it was being cached, I re-ran the web performance test.

Nice! The load time was nearly cut in half! This is a good step in the right direction.

Find slow loading files

Next step I took was to start working down and identifying the next biggest bottlenecks to the site. The waterfall chart was a big help in this regard. The next biggest file that was killing our performance was our CSS stylesheet, site.css.

While only 13KB, there were times when it look nearly 3 seconds to load on certain pages. Running the file through a minimizer like YUI Compressor seemed to help. It dropped the load time of the css file well under a half second.

But by far the biggest bottleneck that was slowing down the website performance was a client logo we had uploaded for the homepage.

This big boy was taking nearly 1.5 seconds to load. What’s really interesting is that it was supposed to be the *thumbnail* file converted by Craft’s image transform. Checking out the actual file sizes on the server was really surprising.

Huh? The thumbnail image (logo.png) is nearly three times the size of the original image! Checking out the image I found that the original was actually three pixels shorter than the thumbnail transform. Apparently Craft was sizing the image UP then re-saving it with some really suboptimal settings. Fixing this issue was as easy as uploading a replacement image that I resized myself. After clearing Craft’s cache the load time dropped significantly.

Next on the plate were the header images we used at the top of every page. While they were all relatively small, non of them were being saved as progressive JPGs. For those that don’t know, progressive JPGs tell the browser to load high quality images in a series of passes. What’s great is the browser isn’t blocked from loading the rest of the page because of a large JPG.

After these steps the pages on Anecka were loading much faster, but I felt like there was more I could do. Now the low hanging fruit was out of the way, time to really kick the performance optimization up a notch.

Gzip Compression

Ah Gzip compression . . . if you don’t do anything else to optimize the performace of your site, at least do this. Basically gzip compression this tells your web server to compress every byte before it sends it to the browser. Every web server supports gzip, every browser supports gzip. It’s free, your hosting provider WANTS you to use gzip (saves them bandwidth). Use gzip.

You don’t even need to modify your CMS (which is good because Craft doesn’t have a setting for gzip). A few simple lines in your .htaccess file will do it. In fact Ben Parizek of BarrelStrength has already done the work for us and created an awesome .htaccess file for Craft.

Taking the settings Ben put together, I modified my .htaccess file like so:

<IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/css application/json AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE text/xml application/xml text/x-component AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml AddOutputFilterByType DEFLATE image/x-icon image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype </IfModule>

After adding gzip I noticed a large bump in performance. Still though the Web Page Performance Test tool was saying I wasn’t taking advantage of browser caching. Diving into the details I found out why.

Setting a max-age or expires on static assets lets the browser know “hey it’s okay if you hang onto these for a while, they’re not going to change.” Without it, the browser just assumes everything coming back is new and doesn’t even bother to check it’s locally stored cache. Fixing this is as easy as a few lines in .htaccess. Again I referred to Ben’s .htaccess file for inspiration.

# If you don't use filenames to version, lower the CSS and JS to something like # "access plus 1 week" or so. <IfModule mod_expires.c> ExpiresActive on # Perhaps better to whitelist expires rules? Perhaps. ExpiresDefault "access plus 1 month" # cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5) ExpiresByType text/cache-manifest "access plus 0 seconds" # Your document html ExpiresByType text/html "access plus 0 seconds" # Data ExpiresByType text/xml "access plus 0 seconds" ExpiresByType application/xml "access plus 0 seconds" ExpiresByType application/json "access plus 0 seconds" # Feed ExpiresByType application/rss+xml "access plus 1 hour" ExpiresByType application/atom+xml "access plus 1 hour" # Favicon (cannot be renamed) ExpiresByType image/x-icon "access plus 1 week" # Media: images, video, audio ExpiresByType image/gif "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType video/ogg "access plus 1 month" ExpiresByType audio/ogg "access plus 1 month" ExpiresByType video/mp4 "access plus 1 month" ExpiresByType video/webm "access plus 1 month" # HTC files (css3pie) ExpiresByType text/x-component "access plus 1 month" # Webfonts ExpiresByType application/x-font-ttf "access plus 1 month" ExpiresByType font/opentype "access plus 1 month" ExpiresByType application/x-font-woff "access plus 1 month" ExpiresByType image/svg+xml "access plus 1 month" ExpiresByType application/vnd.ms-fontobject "access plus 1 month" # CSS and JavaScript ExpiresByType text/css "access plus 1 year" ExpiresByType application/javascript "access plus 1 year" </IfModule>

You can set the access by any amount you want. If you predict you’ll change your css often you may want to shorten the timeframe. For more about mod_expires check out this great article.

After our Performance Optimization

The overall performance gains after making these fairly simple optimization changes was shocking.

Remember how the portfolio page was loading in 7.8 seconds before? It now regularly loads in under 2 seconds. That’s a 76% gain in performance!

The homepage has a performance gain of 28% and loads in roughly 2 seconds, which is noticeability faster (rule of thumb is people notice if things are 20% faster than before). Part of the issue is I have dynamic content on the homepage that I’m not caching, although I’m seriously considering optimizing the page to cache that content as well.

Want to optimize the performance your Craft CMS site? I’ve put together a Free checklist that will take you through step by step on how to speed to turbo-charge Craft. Just click the button.