How to make your SilverStripe website lightning fast

Page speed is one of the factors that Google uses to rank websites in their organic search results. This is for good reason as UX studies found that page speed has a huge impact on user engagement. Optimizely recently published a study where they found that even a delay in page load time of only 4 seconds reduces the number of page views by 11%.

Whether you measure how long it takes to fully load a page ("page load time") or how long it takes for the web server to respond ("time to first byte") doesn't matter. The faster your website loads, the better. Most of the things covered in this post target the time to first byte, with a few exceptions.

What makes a website slow?

When a request for a website is submitted in a browser, a lot is happening.

First, the DNS of the domain name requested needs to be resolved. This can take anything between 10 and a few hundred milliseconds. With the IP received from the DNS the web server can be contacted and the HTML document requested. The webserver then builds that HTML page, which usually takes around 300ms on an average SilverStripe installation. Then, that HTML document is sent back to the browser. The time it takes until that first byte of the HTML document reaches the browser is called "time to first byte". Google recommends a time to first byte (or "server response time") of 200ms or less. That's really fast and can't be achieved by just putting SilverStripe on a shared hosting without optimisation.

Once the browser receives that document it will be parsed. All the linked resources like CSS and javascript files as well as images etc will then be requested and downloaded. This again takes time. At the time of this writing the average website takes about 5 seconds to fully load.

What can be done to make SilverStripe fast?

There are a few things that can be done to make your SilverStripe site faster. A few of them have been mentioned by Simon on the SilverStripe Blog.

Move away from shared hosting

Shared hosting usually doesn't have the power to cope with a high performing SilverStripe site, or any standard CMS really. The shared hosting providers have hundreds of sites on a single machine which leaves very few resources to a single site. This will impact the performance of all sites when traffic to only one of them increases.

Moving to a dedicated VPS will improve the performance of your site significantly. I usually use Micron21 servers for my customers and can highly recommend them.

Opcode Cache

One of the drawbacks (and advantage at the same time) of PHP is that the PHP code is only translated into machine readable code when the code is executed. The advantage of this is of course that you don't have to compile the code for deployment like with other programming languages like Java or Go. But it takes time for the PHP code to be compiled into opcode and the HTML to be generated.

An opcode cache keeps the generated opcode in memory or the file system so the files only need to be re-parsed when they change. You can use xcache if you're using an older version of PHP. PHP 5.5 and above has opcode caching built-in and the extension is also available for PHP 5.2, 5.3 and 5.4 in PECL.

For most sites this will give quite a performance boost.

Profiling

To find bottlenecks in your code you can use profiling of the requests. There are two options I know of, XHProf and XDebug. By analysing the profiling data, you will be able to see what's slowing down requests so you know where to concentrate your efforts.

Add caching

There are a few options that can be explored to cache some or all the generated HTML.

SilverStripe partial caching

The partial caching is a great option for parts on the page that don't change very often but still need a lot of database queries to build. A good example for this is the navigation, where a lot of pages need to be requested from the database for each request. Also, listings of other data objects can use a lot of database requests.

You can wrap sections of your layout or even the complete layout template in a <% cached %> tag and use aggregates of the elements included as a cache key. For a navigation section the cache key would look something like this:

<% cached 'navigation', $List('SiteTree').max('LastEdited'), $List('SiteTree').count() %>

This will reduce the database queries used to build your pages by up to 80%.

You'll find the documentation on partial caching in the SilverStripe docs.

SS_Cache

SS_Cache gives you the ability to cache arbitrary data in the SilverStripe cache. This is particularly useful when you load data from a third party and don't need that data to be reloaded for every request. SS_Cache will store the data and only re-load it when it isn't in cache already or when it has expired.

Static publishing

For sites that rarely change you can use static publishing. This saves the generated HTML of your pages in the file system and requests bypass SilverStripe completely, resulting in a very fast site.

The drawback of this is that you can't have any interactive elements on your site such as forms on your site for static publishing to work, because there is no interaction with SilverStripe to submit and process your form data. If that's the case, partial caching (see above) is more suitable.

Browser caching with .htaccess

You can improve page load times for returning visitors by telling browsers to cache resources like images and scripts. This is not SilverStripe specific and can help with any website setup. If you're using Apache, you can add something like the following to your .htaccess file:

### CACHING START ###
    <ifModule mod_expires.c>
        ExpiresActive On
        
        ### IMAGES/FLASH/AUDIO/VIDEO 1 MONTH ###
        ExpiresByType image/x-icon A2592000
        ExpiresByType image/jpeg A2592000
        ExpiresByType image/png A2592000
        ExpiresByType image/gif A2592000
        ExpiresByType image/svg+xml A2592000
        ExpiresByType application/x-shockwave-flash A2592000
        ExpiresByType video/mp4 A2592000
        ExpiresByType video/quicktime A2592000
        ExpiresByType video/x-f4v A2592000
        ExpiresByType audio/mpeg A2592000
        ExpiresByType audio/x-aac A2592000
        ExpiresByType audio/mp4 A2592000
        
        ### CSS/JS 1 WEEK ###
        ExpiresByType text/css A604800
        ExpiresByType text/javascript A604800
        ExpiresByType application/javascript A604800
        ExpiresByType application/x-javascript A604800
        
    </ifModule>
    <ifModule mod_headers.c>
      <filesMatch "\.(ico|jpe?g|png|gif|swf|flv|mp4|mov|f4v|mp3|aac|m4a)$">
        Header set Cache-Control "max-age=2592000, public"
      </filesMatch>
      <filesMatch "\.(css)$">
        Header set Cache-Control "max-age=604800, public"
      </filesMatch>
      <filesMatch "\.(js)$">
        Header set Cache-Control "max-age=604800, public"
      </filesMatch>
      <filesMatch "\.(woff|eot|ttf|otf|svg)$">
        Header set Cache-Control "max-age=2592000, public"
      </filesMatch>
    </ifModule>
### CACHING END ###

Build your website responsibly

With responsive web design, there is a lot that can be done to improve the page load time of your website. Not every device should receive your fully-fledged site with all the huge images and nice-to-have functionality. Sometimes (a lot of times) less is more.

I highly recommend Scott Jehl's book Responsible Responsive Design from A Book Apart. It takes you through all the things that need to be considered when delivering truly responsive websites taking into account not only screen size, but also different network speeds and device capabilities.

What if that's not enough?

There are situations where you've done everything reasonably possible or where a site is performing well in Australia, but not in the US. I had a client who operates in Australia with their customer base being in both Australia and Europe. I had to come up with a solution that served their customers well, without blowing their budget. I was considering caching and CDNs and tried a few things. But there just wasn't a solution that covered all bases. Until I discovered section.io.

section.io to the rescue

section.io is a globally distributed Varnish cache on AWS. The advantage over other CDN solutions is that you can cache everything, also the HTML documents of your website. And because section.io has caching nodes all over the world, it makes your website fast from anywhere. That's also different from installing your website on AWS, because when you do that your website will still be in one single AWS location, e.g. Sydney, which doesn't help much when your site is requested from Europe or the US.

section.io has an API that lets you manage your cache. (section.io have recently changed their payment model again. The feature comparison table states that the API is not available for the Essentials package anymore. Steward from section.io has assured me that they are not imposing the feature gating for the Essentials package for the time being, which means that the API is still available for all packages.)

I have built a SilverStripe module for section.io that automatically clears your cache when you publish new content in SilverStripe. It is open source and available on GitHub. All the instruction on how to use it are in the docs.

Since using section.io on my own as well as some customer websites, I have seen a massive decrease in time to first byte as well as page load times. The average time to first byte for my own website is below 200ms from Australia, Europe and the US. The same is true for my client's website mentioned above.

Let me know if you have any questions or comments, happy to help.

Posted in Development; Tagged SilverStripe, SEO; Posted by Florian Thoma