How to make your Silverstripe website lightning fast
- Publication
- Author
- Florian Thoma
- Categories
- Reading time
- 7 minutes
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"), it doesn't matter. The faster your website loads, the better. This blog post mostly covers 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 for that first byte of the HTML document to reach 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 CMS. 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.
Another option is to use the Silverstripe Platform, which gives you not only hosting on AWS, but also 24/7 support, code management, excellent deployment tools, monitoring and more.
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 that you don't have to compile the code for deployment like with other programming languages such as 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.
Once Silverstripe 4 is stable and can be used in production later this year, you can use PHP 7, which is significantly faster than PHP 5.x. Unfortunately Silverstripe 3.x doesn’t work with PHP 7 out of the box. There is a patch available for Silverstripe 3 from wernerkrauss that you can load in via composer, but I haven’t tested this.
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 of 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 retrieved from the database for each request. Also, listings of other DataObjects 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 ###
For nginx you would add something like this in your vhost configuration:
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
add_header Cache-Control "public";
}
# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1y;
add_header Cache-Control "public";
}
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, including the HTML documents of your website. 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, 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 instructions 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.
Other CDNs
Of course, there are tonnes of other CDN options available. I found two more that have Silverstripe integration:
- If you want to use CloudFlare, there's a Silverstripe CloudFlare module available which automatically purges the cache for pages that are published or unpublished. It also handles purges to the site tree and purges the whole tree if you change the title or URL segment of a page.
- If you want to use your own Varnish installation, Carey Sizer has released a Silverstripe Varnish module, using some inspiration from my module for section.io.
updated 25/1/2017 after re-publish on Silverstripe.org