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.

PageInsights Desktop

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

PageSpeed Insights Desktop

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

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.

WebPage Test before optimization

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.

WebPageTest Portfolio

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.

Portfolio page performance

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.

After Craft Cache

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.

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.

Screen Shot 2014-08-26 at 8.52.41 PM

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.

Screen Shot 2014-08-26 at 9.07.51 PM

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.

Screen Shot 2014-08-26 at 9.25.25 PM

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.

After web performance optimization

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.

 

Download the Free Performance Checklist

 



100% privacy guaranteed