Being the lean platform it is, Ghost is rather quick without any additional tweaks, so why spend any time at all extracting every last bit of performance? Especially when most users will never even notice it? Because I think it is fun and educational as it touches on basically all aspects of hosting a website.
Before we can begin tweaking, we should first take a baseline so we can later see the fruits of our labor. As you can see plain Ghost is already plenty fast by itself.
For the curious reader, the Pingdom results are still available.
One of the things Pingdom screamed at the loudest was the fact Gzip is not enabled. Unfortunately Ghost does not support Gzip natively, luckily enabling Gzip in Nginx is an easy feat.
This snippet is used to run an Nginx container and making sure Traefik knows about the new service. Make sure you remove the Traefik labels from the Ghost container.
docker-compose up and the blog should work exactly the way it did 10 minutes ago, but now requests are proxied via Nginx.
Now that Nginx is working as expected we can tune the Gzip parameters. I find the official documentation is an excelent starting point on how to accomplish this. Bottomline, this is the new config file.
docker-compose up and Pingdom is happy.
Using a CDN
Using a CDN can help offloading some of the work load to a third party as well as speeding the loading of assets files up by moving them physically closer to the user.
For the CDN I like using Bunny CDN as it is cheap, fast and easy to use. It also has cutesy loading animations with is a small plus as well.
In Bunny creating a "pull zone" is straight forward. This effectively checks if Bunny has the requested path in its cache already, if not it will be requested from the origin URL. This makes the setup simple to configure as all you need to change is the asset domain.
Optionally an custom domain can be used by creating a subdomain with a CNAME record pointing to the original Bunny URL. In this case cdn.niek.tech points to niek-tech.b-cdn.net. SSL is handled by Bunny using a certificate provided by Let's Encrypt.
Once the pull zone is configured and working, it needs to be implemented on asset URLs. This could be done by forking the theme and editing the URLs in the relevant templates and if I were using a custom theme that would be the only option. However, if at all possible I would like to keep using the vanilla Ghost container without any modifications.
Editing responses with Nginx
The real reason I choose to use Nginx, is that it supports
sub_filters. This allows to replace strings with another string. Effectively this allows me to replace all occurances of
I accomplished this by adding the snippet below to the location block.
This can be optimized slightly further by ensuring the CDN domain is preloaded. By adding the relevant tags to the HTML head. Luckily, Ghost provides an option to edit the head directly from the CMS under the section Code Injection.
First, the scores after the tweaks.
As before the detailed result are available.
As you can see the Pingdom score is actually lower than it was before the tweaks were applied. This is because Bunny uses Brotli compression where possible, however at the time of writing Pingdom does not detect this and keeps suggesting to enable Gzip compression. If supported by the browser Brotli compression is the preferred method however.
As long as this issue stands, the Pingdom score will actually be lower once Bunny is implemented. This shows the biggest downside of chasing perfect scores on website analysis tools, they often fail to see the big picture and in some cases disregard how an actual user sees and uses the site.
Implementing the suggestions made by these sites can be a fun and worthwhile activity, but should be taken with a grain of salt. As always, be certain you understand the changes you are making.
If you would like to sign up to Bunny CDN, I would much appreciate it if you would my referral link. If you do, I will receive $20 in credits.