How I Optimized My Self-Hosted Ghost Install

These are some of my initial modifications made after transitioning from Wordpress to Ghost. Please bare in mind some of these I know longer utilize after using Draftbox for a period of time.

While not all these are in use after dabbling with Jamstack for a few months, some may find them useful.
While not all these are in use after dabbling with Jamstack for a few months, some may find them useful.

Search Engine Optimization

Straight out of the box, Ghost is a fantastic platform for bloggers and content creators alike, used by well-known tech giant Apple which, of course, has brilliant aesthetics on all their web domains. I did, however, find that some parts of Ghost were a little lacking in some areas, and I wanted to customize some functions. Some of these modifications are for SEO purposes, others for speed and visuals. I've grouped SEO and speed modifications since speed also influences SEO. Just a warning as we head into this guide, I am not a developer. Most of my experience has been with SEO, Wordpress, CDN's. This is what worked for me and I hope that I'll be able to make it a little easier for you to implement.

Compared to WordPress, Ghost is a breeze. SEO features are built-in without having to add endless plugins that create bloat and slow down your website. With Ghost, everything is done either by modifying theme files, code injection, or adding scripts.

There's also some other issues as well that I'd like to see resolved by the Ghost team. When you're a somewhat of a perfectionist, little things will urk you. For example, "Alt" tags for images. At the moment the featured Image isn't allowing you to modify the alt description. In addition, when you upload a gallery, there can be a "figcaption" underneath, but you don't have the ability to add an alt tag to each of those images unlesss you utilize HTML to manually do so yourself.

Another thing you have to be careful of is using too many tags, as of course you'll see duplicate content across URL's, but in some instances it doesn't really matter and search engines have evolved enough to distinguish between duplicate content.

Tags are also annoying AF right now in some aspects. For example, for the I Live For Me section of this site, I actually just created a custom .hbs and am working on the same for other sections. The issue with that really is that then I have to create the Metadata and items manually for that custom page or collection because I want to change the Open Graph data from "Article" which all Ghost posts are right now, to "Music" values, and in some cases, "Video". This is where I find Ghost gets messy and the extra work having to create these pages and do things manually, when Ghost could just add functionality to change post types would be the quickest solution. Also they don't really take feedback or suggestions via the Github rep.

Structured Data

Ghost comes with some structured data right out of the box, including Article and Website, among meta descriptions that are customizable for either Facebook or Twitter. I wanted to go beyond this as I have some experience with Structured Data, and I'm obsessed with using it to create rich snippets in Google. Beyond that, I also use new types of pending entries that will someday become recognized Schema.org types.

Explore the search gallery | Search for Developers | Google Developers

So what's the best way to implement this? You could write your code, of course, but I chose to go with Schema App. They offer a free 14-day trial, at which time you can utilize their JavaScript integration. Still, before it ends, you can choose to export the structured data you've created into JSON-LD and use the code injection function to add it to relevant parts of your website. You don't have to stay with their subscription model. I do because they have functions that make it easy to maintain and update structured data as your site changes, like URI health and Analyzer. Any updates you make to your data entries can be done quickly and updated as it's served from their CDN. The integration is just two lines of code that you input in code injection.

I will go into this in-depth in the article Deploying Schema.Org Schema via Schema App as it was definitely a learning-curve situation for me when I first started. Learning how to link your content together especially using the 'sameas' entry and then linking back to your domain from those URL's is a good practice to group your content and ownership on other platforms together and let Google know your other identities.

Important Edit:

While Schema app is excellent for creating Schema, it's been pretty unstable with Ghost via the Javascript tag function. I'd create Schema and it wouldn't show up on the site. I've reached out to customer service almost two weeks ago and they said they were checking with devs. I've actually been through this process with them before and the support is extremely lacking while you continue to pay for a subscription.

For now, the workaround works great and I wouldn't need to continue paying for the expensive subscription. Instead of using code injection to place completed Schema JSON-LD into posts, I've offloaded them into dedicated .js files in a seperate folder in my ghost install, and then referenced them with src, and used the defer tags which actually works out quicker. Although argueably I could offload this to Digitalocean spaces as it comes with a CDN, will look into this afterwards.

Pro Sitemaps

So you can again totally implement this yourself or go with the native Sitemap that Ghost creates, I just wanted to add an image and a video sitemap to the blog, so I chose to go with Pro Sitemaps and try that out. With the created Sitemap, I uploaded it to the root of Ghost's theme. I added it to Search Console, Bing Webmaster and Yandex Webmaster, all of which you can make an automatic submission with Pro Sitemaps, or you can do this manually.

I suggest instead, even if you do use automatic submission to create and monitor your website in Webmaster Tools just to make sure everything is working as it should and that there are no broken links, etc.

URL Routing and Collections

This was actually initially a mess for me to figure out how to setup. Your Routes.yaml file in Ghost lets you re-route and modify the URL structure. I was wilding because I had tried to do it over and over again, when I realized that in the first entry for the collection I had to tag it with -tag so that the subsequent collection entries would have content to grab from. So I sorted this website into the main home page which house all entries minus Podcast and Blog so that they can be served on their own URL, but also not be on the homepage, avoiding duplicate content.

HSTS Preload List

Once you've enabled Full SSL and the required elements for HSTS you can head over and submit your domain URL to the preload list. Most major browsers will base their preload list on Chrome's.

Google Publisher Center

If your website publishes content regularly you can apply and create your publication so these will be displayed in Google news. The process is straightforward and once its setup its a set-it and forget-it deal. You should already have white and dark mode versions of your logo to submit. If you have trouble getting articles from your feed, try signing up with Feedburner and then submitting that feed.

Facebook

For Facebook, I created an app that is linked to my Facebook Page for the blog, and added a meta name with the app Id into the headers with code injection. If you query your URL on Facebook's debugging it will actually warn you that you're missing the app ID. Make sure as well once you create your app to add your domains to the app's list of domains.

Amp.hbs

Ghost automatically creates Amp pages of posts (Not pages) and it already looks pretty good. I just wanted to modify the colours and look of the amp page a little bit more to match the main pages.


Content Delivery Networks

Cloudflare

Cloudflare has been my CDN of choice for years. They are always growing and adding new features, and the most basic subscription is free and we can't argue with free. I've listed some of the aspects of Cloudflare that I use on this domain below.

Page Rules/Caching

After I finished deciding on the structure of the website and the Style, I went ahead and configured Cloudflare caching based on recommendations I researched specifically to Ghost. Again, settings that are appropriate to your website will depend largely on how often you update content.

As I'll talk about again later in this article, images are served on a seperate "cdn.kesen.wang" domain in addition to some audio files. Because its not likely that you will be making and changes to images or audio files you can set the caching to a much longer period then for example, CSS or HTML.

After I transitioned all images to my Digitalocean Space I went ahead and set caching for the CDN subdomain to six months. Google official recommends six months to a year. Keep in mind that Spaces also has a built in CDN so I'm using that in addition to Cloudflare. The only difference being that spaces by default only allows you to select a cache period of up to a week. So i assigned the whole subdomain to cache for half a year when its served through Cloudflare.

In addition to this, this domain is also set to cache everything but with a browser TTL of five hours currently. I've also used page rules to create cache bypass on any admin pages, login url's, as well as "p/" as these are the preview links ghost creates. And if I really want to see the change on the website immediately I just perform a custom purge of the URL in the Cloudflare dash, rather then purging the whole domain which will slow things down.

Advanced SSL Certificate

Again, this is something that you don't really need and is just more something I like to use, rather then share my SSL cert with other websites.

Argo

Argo routes your viewers quickly to the closest data Center and can decrease the overall amount of time someone is waiting to load your website.

Privacy-First Analytics

In addition to not slowing down your website with script like Google Analytics, Cloudflare maintains a privacy-first analytic programme that is nowhere near as invasive as Google Analytics. Seriously, you don't need to know the gender of your viewers.

Access

This is what I used to secure my login URL as it's hard to change and the Access implementation is free and works great. Now when you navigate to the login URL you'll have to get a one-time code to login which works only with a set of emails that I've whitelisted. It can also be setup to use Facebook, GitHub or other authentication services in addition to emails, I just found whitelisting emails was the easier route.

Workers

Also went along with a tutorial for Workers deployed using Wrangler to create the function of automatically clearing the cache via Webhook when a post is updated.

I also used another Workers script that allows for injection of a Content Security Policy. (I'm still figuring this out, read more in Security Headers.

TLSV1.3

Cloudflare allows you to depreciate older versions so I went with minimum of TSLV1.2, and also made these adjustments on a server level editing the nginx config file to remove support for earlier versions.

Cloudflare Stream

This is probably the quickest and easiest way to host videos on your domain without third party branding, and also allows for preloading.


Other Content Delivery Networks

jsDelivr

I changed the themes references to Jquery from its website to jsDelivr's CDN to cut down on multiple outcalls to different domains.

Digitalocean Spaces

Since I already have this self-hosted Ghost Install with Digital ocean, it made sense to use them for storage. I created a separate subdomain of kesen.wang with the CNAME "cdn," so now all images and audio files uploaded here are hosted in Spaces, which comes with its own CDN.

This is accomplished using a storage adapter with Ghost; a simple GitHub repo I used can be found here; setup is a breeze and will ask you to provide the subdomain, the location, and critical Secret, which can be generated in Spaces. After setup, any images you upload will be stored in Spaces. Keep in mind this is not retroactive, and pre-existing photos will need to be reuploaded onto spaces. This can be frustrating, especially when you have pre-existing structured data that links to the old image URL.

Not that it's super complicated, but if you are not using their name servers and your blog is hosted elsewhere, you'll need to bring your SSL certificate over and upload it in Spaces. This means creating an origin certificate and copying the private key, certificate and CA for those on Cloudflare. Keep in mind that if you already have an origin certificate uploaded on your server, you'll need to update that to the same one you use for Spaces.

Security Headers

For a relatively straight-forward process, you just add the lines below to your ghost config file found in /etc/nginx/sites-available/.

add_header X-XSS-Protection "1; mode=block";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options nosniff;

Adding this code into your Ghost config file located in sites-available will ensure your security headers are in place.

After searching a bit more I found a workers script from Cloudflare to enable CSP on this Ghost blog quite easily. This adds all the needed headers via Cloudflare. This can be found on Github here.


Write a caption (optional)

Hosting/Storage

Ghost Pro is awesome and I would recommend that to anyone who has enough funds to shell out for the monthly subscription fee. That being said, Digitalocean has made it incredibly easy to host your own Ghost install with minimal effort. If you're coming from Wordpress than this will seem effortless compared to the regular maintence Wordpress requires from broken SSL, Plugin incompatibility or design nightmares.

Currently I'm using a $40.00 monthly install, but I can downgrade should I chose, if you're using a good CDN you don't 'need' to have a super fast droplet. I find Digitalocean easy to use, a good UI and they have really great tutorials should you get suck. They also offered an upfront credit on new accounts when I signed up so it was great to get used to their offerings. In fact, I'm still actually using the credit at the time of posting.

Protonmail

While Ghost prefers you use Mailgun to send emails, I installed it and gave it a try but I don't like the idea of a third-party reading through my messages and without Zero-Trust Encryption. For my email needs I use Protonmail.

Podcast Specific

To host my Podcast rather than on this website directly I chose Anchor FM, mainly for its ease of use and easy distribution of my podcast.

As well I added this simple line of code into the section of the website to enable the Apple Smart Banner. Of course the X value is the app id specific to your podcast.

Graphics/Design

This is a pretty vast category. You can use any program or app to create visuals for your website. I've listed a few that I find helpful below, most will work on Mobile because I so most of my writing and editing on my iPhone.

Speed Modifications

PurifyCSS

After customizing and settling on the layout of the website, I used this tool to remove unused CSS from the theme. Of course, now when updates are released I need to edit back in the mods I made to the theme, but it's not much work and I always keep a backup of the theme before I make changes or upgrade.

Nginx Cache

Amp.hbs Modifications

Testing

After you make changes to your site, it's worth it to test how those changes are affecting your performance. I've listed some of the recommendations for testing that I use below.

WebPageTest - Website Performance and Optimization Test
Run a free website speed test from around the globe using real browsers at consumer connection speeds with detailed optimization recommendations.
Rich Results Test - Google Search Console
GTmetrix | Website Performance Testing and Monitoring
GTmetrix is a free tool to test and monitor your page’s performance. Using Lighthouse, GTmetrix generates scores for your pages and offers actionable recommendations on how to optimize them.

Just remember to choose one and stick with it as you optimize your site. They all use different criteria to grade sites, so if you keep hopping from test to test you'll find it very hard to know what adjustments are resulting in an improvement.