Optimize Ghost Blog Performance Including Rewriting Image Domains to a CDN
Sometimes, ghost blogging is too lean for our requirements. Follow these steps to optimize a Ghost Blog's performance while keeping it lean and resourceful.
Join the DZone community and get the full member experience.
Join For FreeThe Ghost blogging platform offers a lean and minimalist experience. And that's why we love it. But unfortunately, sometimes, it can be too lean for our requirements.
Web performance has become more important and relevant than ever, especially since Google started including it as a parameter in its SEO rankings. We make sure to optimize our websites as much as possible, offering the best possible user experience. This article will walk you through the steps you can take to optimize a Ghost Blog's performance while keeping it lean and resourceful.
When we started working on the appfleet blog we began with a few simple things:
Ghost Responsive Images
The featured image in a blog has lots of parameters, which is a good thing. For example, you can set multiple sizes in package.json
and have Ghost automatically resize them for a responsive experience for users on mobile devices or smaller screens.
xxxxxxxxxx
"config": {
"posts_per_page": 10,
"image_sizes": {
"xxs": {
"width": 30
},
"xs": {
"width": 100
},
"s": {
"width": 300
},
"m": {
"width": 600
},
"l": {
"width": 900
},
"xl": {
"width": 1200
}
}
}
And then, all you have to do is update the theme's code
xxxxxxxxxx
<img class="feature-image"
srcset="{{img_url feature_image size="s"}} 300w,
{{img_url feature_image size="m"}} 600w,
{{img_url feature_image size="l"}} 900w,
{{img_url feature_image size="xl"}} 1200w"
sizes="800px"
src="{{img_url feature_image size="l"}}"
alt="{{title}}"
/>
Common HTML Tags for Performance
Next, we take a few simple steps to optimize Asset Download Time. That includes adding preconnect
and preload
headers in default.hbs
:
xxxxxxxxxx
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin="anonymous">
<link rel="preconnect" href="https://cdn.jsdelivr.net/" crossorigin="anonymous">
<link rel="preconnect" href="https://widget.appfleet.com/" crossorigin="anonymous">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css?family=Red+Hat+Display:400,500,700&display=swap" />
<link rel="preload" as="style" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13.0/css/all.min.css" />
As we load many files from jsDelivr to improve our performance, we instruct the browser to establish a connection with the domain as soon as possible. The same goes for Google Fonts and the sidebar widget that was custom coded.
Most often than not, users coming from Google or some other source to a specific blog post will navigate to the homepage to check what else we have written. For the same reason, on blog posts, we also added prefetch
and prerender
tags for the main blog page.
That way the browser will asynchronously download and cache it, making the next most probable action of the user almost instant:
xxxxxxxxxx
<link rel="prefetch" href="https://appfleet.com/blog">
<link rel="prerender" href="https://appfleet.com/blog">
To solve this problem we took two steps. Lazy load the images and use a CDN. The issue is that the Ghost doesn't allow to modify or filter the contents of the post. All you can do is output the HTML.
The easiest solution to this is to use a dynamic content CDN like Cloudflare. A CDN will proxy the whole site, won't cache the HTML, but cache all static content like images. They also have an option to lazy load all images by injecting their Javascript.
But we didn't want to use Cloudflare in this case. And didn't feel like injecting third-party JS to lazy load the images either. So what did we do?
Nginx to the Rescue!
Our blog is hosted on a DigitalOcean droplet created using its marketplace apps. It's basically an Ubuntu VM that comes pre-installed with Node.js, NPM, Nginx, and Ghost.
Note that even if you don't use DigitalOcean, you are still recommended to use Nginx in-front of the Node.js app of Ghost.
This eventually makes the solution pretty simple. We use Nginx to rewrite the HTML, along with enabling a CDN and lazy-loading images at the same time, without any extra JS.
For CDN, you may also use the free CDN offered by Google to all AMP projects. Not many people are aware that you can use it as a regular CDN without actually implementing AMP.
All you have to do is use this URL in front of your images:
https://appfleet-com.cdn.ampproject.org/i/s/appfleet.com/
Replace the domains with your own and change your <img>
tags, and you are done. All images are now served through Google's CDN.
The best part is that the images are not only served but optimized as well. Additionally, it will even serve a WebP version of the image when possible, further improving the performance of your site.
As for lazy loading, you may use the native functionality of modern browsers that looks like this <img loading="lazy"
. By adding loading="lazy"
to all images, you instruct the browsers to automatically lazy load them once they become visible by the user.
And now the code itself to achieve this:
xxxxxxxxxx
server {
listen 80;
server_name NAME;
location ^~ /blog/ {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host "appfleet.com";
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://127.0.0.1:2368;
proxy_redirect off;
#disable compression
proxy_set_header Accept-Encoding "";
#rewrite the html
sub_filter_once off;
sub_filter_types text/html;
sub_filter '<img src="https://appfleet.com' '<img loading="lazy" data-fr-src="https://appfleet-com.cdn.ampproject.org/i/s/appfleet.com';
}
}
First, we disable compression between Node.js and Nginx. If not done, Nginx can't modify the HTML if it comes in binary form.
Next, we use the sub_filter
parameter to rewrite HTML. Additionally in line#19 above, we enabled both the CDN and lazyloading. Reload the config and you are good to go.
Check Appfleet to see the configured performance in real-time.
I would be delighted to know your experience with the configuration above.
Published at DZone with permission of Sudip Sengupta. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Writing a Vector Database in a Week in Rust
-
How To Approach Java, Databases, and SQL [Video]
-
RBAC With API Gateway and Open Policy Agent (OPA)
-
Comparing Cloud Hosting vs. Self Hosting
Comments