Turbo-Charge Grav with a CDN

Take advantage of content delivery networks to speed you site up!

5 minutes

Grav is already one of the fastest CMS options available, but it's possible to easily transform your Grav-based site into a world-class performer. A CDN or Content Delivery Network and some new Grav features can make this possible.

Let's take a real-world example to show how this can be done. And what better example than the getgrav.org website itself.

Before starting on this endeavor I ran a test using the popular webpagetest.org service to get an initial benchmark:

The results were a mixed bag to be honest. Grav is quick, but we were not taking advantage of browser caching, some compression settings, nor a CDN for static files.

Setting up the CDN

A content delivery network (CDN) is a system of distributed servers (network) that deliver webpages and other Web content to a user based on the geographic locations of the user, the origin of the webpage and a content delivery server. -- webopedia.com

You can use pretty much any Pull Zone style CDN, but we chose MaxCDN as they are pretty much the leader in CDN services and have a great set of control panels, tools, and APIs to make the process of integrating as simple as possible. They are also very open source friendly and provide CDN services for many great open source projects such as Bootstrap, FontAwesome, and jQuery.

Creating the initial Pull Zone is a simple affair with MaxCDN by following their detailed instructions.

After the zone is created, you will probably want to tweak a few settings:

  • Setup a custom DNS entry with name cdn.getgrav.org that aliases to our provided CDN name (optional)
  • Set the Default Cache Time and Cache-Control Header both to 7 days
  • Set the Edge Setting options for Strip All Cookies and Ignore Cache Control

CDN Plugin for Grav

Pull Zone CDNs work by having the request for static resources (CSS, JS, Images, etc) made via the CDN and then caching and distributing those files among the CDN network to ensure that users always receive a cached copy from the closest server. This ensures your site is fast no matter the geolocation of the end user. In order for this to be a seemless process in Grav, we need a plugin to rewrite standard local URLs such as:

<img style="" src="/user/pages/02.blog/state-of-grav-apr2015/github-trending.png">

to:

<img style="" src="http://cdn.getgrav.org/user/pages/02.blog/state-of-grav-apr2015/github-trending.png">

This is can be done pretty easily with Grav's plugin hooks and a little Regex magic. Yesterday I wrote a simple plugin eloquently named Grav CDN Plugin. To install this plugin simply install it via GPM:

$ bin/gpm install cdn

This will install and activate the CDN plugin, If you view the source, you will see the URLs for CSS, JS, and images are being rewritten to use a specific pull zone name. You need to update this to use your own domain as outlined by your CDN provider. To accomplish this simply create a new file: user/config/plugins/cdn.yaml and put the following:

enabled: true
pullzone: your-cdn-domain.com

Where your-cdn-domain.com is either the custom domain name you setup, or the domain name as provided to you. In our case it was cdn.getgrav.org. Save this file to ensure your changes get picked up by Grav.

Enable GZip Compression

A feature that we recently added to Grav was the ability to enable GZip compression for the page. This compresses the HTML file on the server and sends this smaller compressed file to the browser, which means less data transfered, and consequently faster performance.

Simply add gzip: true to your cache: section of your user/config/system.yaml file:

cache:
  gzip: true

Expires Tags

One thing we were missing in Grav was the expires tag for the rendered pages. We added this (will be available in Grav 0.9.24+) and that expires time is configurable vis the system.yaml configuration. This is automatically added and defaults to 7 days which is minimum recommended setting.

You can modify this value by providing another expires: value in the pages: section of your user/config/system.yaml file.

The expires tags on the static resources will be set automatically by the CDN and the settings we setup previously will be used to also set these expires dates to 7 days in the future.

Timestamps

One feature of a CDN is that it caches your static resources to ensure that they can be served as quickly as possible. The downside of this however, is that if you change one of these files, the CDN doesn't necessarily know to grab the latest copy, and will still serve the outdated version to users.

You have two options here.

  1. You can manually clear the CDN cache via the CDN Services's control panel
  2. You can use a timestamp in the query string of the resource URL to signify unique versions.

In Grav version 0.9.24+, there are two options to enable these timestamps to be automatically added to Assets and Media objects. These settings can be toggle in your user/config/system.yaml file via:

assets:
  enable_asset_timestamp: true

media:
  enable_media_timestamp: true

When enabled the URLs will look something like this (notice the ?21e95106 appended to the URL):

<img style="" src="http://cdn.getgrav.org/user/pages/02.blog/state-of-grav-apr2015/grav-themes.png?21e95106">

Results

Ok so now we have completed our fixes let's test again with the same webpagetest.org site and see how we faired:

Wow, That's a great improvement. We have now aced every category and the detailed output tells the true story:

Details:
  First Byte Time (back-end processing): 100/100
  122 ms First Byte Time
  126 ms Target First Byte Time

Use persistent connections (keep alive): 100/100

Use gzip compression for transferring compressable responses: 100/100
  93.3 KB total in compressible text, target size = 93.3 KB - potential savings = 0.0 KB

Compress Images: 100/100
  108.0 KB total in images, target size = 108.0 KB - potential savings = 0.0 KB

Use Progressive JPEGs: 100/100
  108.0 KB of a possible 108.0 KB (100%) were from progressive JPEG images

Leverage browser caching of static assets: 90/100
  FAILED - (No max-age or expires) - http://fonts.googleapis.com/css?family=Montserrat:400|Muli:300,400|Inconsolata
  WARNING - (2.0 hours) - http://www.google-analytics.com/analytics.js

Use a CDN for all static assets: 100/100
  CDN's Used:
  cdn.getgrav.org : NetDNA
  fonts.googleapis.com : Google
  fonts.gstatic.com : Google
  www.google-analytics.com : Google

The only non 100/100 score comes from resources provided by Google CDNs which our outside of our control. Overall a very satisfactory result!

Just a few simple steps and the addition of a good CDN solution were able to propel Grav to a truly world-class performer!

Let us know below if you have any questions.