How to make Ghost and Netlify work with /blog (hint: Cloudflare Workers)

Kan Yilmaz
5 min readMay 31, 2020

--

Ghost is a really good CMS, however, using ghost with /blog rather than subdomain is quite a hassle. Even in their pricing page, it is mentioned as “Supporting this setup is non-trivial”.

“It’s also possible to run sites on a subdirectory, like example.com/blog, however this requires customers to run their own self-hosted reverse proxy with a custom configuration. Supporting this setup is non-trivial”
Ghost

Why would someone prefer example.com/blograther than blog.example.com though?

The answer is SEO.

SEO

Google will treat both subdomain and subdirectory equally. This seems like a good thing, but it’s not. All the hard work you did writing blogs, it only increases your blog.example.com's ranking. If your blog was likeexample.com/blog your landing page would also get that bump resulting in more leads!

If you have bought your domain from Netlify good luck! We’ll be explaining how you can achieve /blogwhile using Netlify and Ghost. Firstly, you’d need to change your DNS provider to Cloudflare where you can have custom rules.

DNS

Initially, move your domain to Cloudflare. There are good posts explaining this process. To put it simply add the following DNS records.

A example.com 104.198.14.52

CNAME some-text-provided-by.netlify.com

Normally, it is recommended to only use DNS from Cloudflare but not the CDN itself. Netlify has it’s own CDN and cache invalidation. These are really important features of Netlify, but to make this setup work, we’d need to used Cloudflare as a reverse proxy.

orange cloud the records

Enable orange cloud for both of the records you added.

Setup Blog

If you don’t have a blog yet, follow the official guidelines. Get a blog ready. Most likely you’ll have a myblog.ghost.org or blog.example.com. Both are fine. Make sure it works.

The problem we’ll face is, how do we redirect example.com/blog to myblog.ghost.com. You’ll need a reverse proxy. Netlify used to handle all those annoying bits and it’s not customizable. You have two options, (1) have a custom reverse proxy server, make sure the DNS point to it, then you point it back to Netlify (2) Use Cloudflare Workers as a reverse proxy.

Note: If you are wondering if redirects would work, the answer is they won’t. Search engine crawlers understand redirect and give the SEO ranking to the destination URL.

Cloudflare Workers

Go to Cloudflare Dashboard — Workers — Manage Workers — Create a Worker

Add the following code

// keep track of all our blog endpoints hereconst myBlog = {   hostname: "blog.example.com", // TODO change this to your blog domain   targetSubdirectory: "/blog",   assetsPathnames: ["/public/","/content/", "/assets/"]}async function handleRequest(request) {   // returns an empty string or a path if one exists   const formatPath = (url) => {      const pruned = url.pathname.split("/").filter(part => part)      return pruned && pruned.length > 1 ? `${pruned.join("/")}` : ""   }   const parsedUrl = new URL(request.url)   const requestMatches = match => new   RegExp(match).test(parsedUrl.pathname)   
// if its blog html, get it
if (requestMatches(myBlog.targetSubdirectory)) { console.log("this is a request for a blog document", parsedUrl.pathname) const targetPath = formatPath(parsedUrl) return fetch(`https://${myBlog.hostname}/${targetPath}`) } // if its blog assets, get them if (myBlog.assetsPathnames.some(requestMatches)) { console.log("this is a request for blog assets", parsedUrl.pathname) const assetUrl = request.url.replace(parsedUrl.hostname, myBlog.hostname); return fetch(assetUrl) } console.log("this is a request to my root domain", parsedUrl.host, parsedUrl.pathname); // if its not a request blog related stuff, do nothing return fetch(request)}addEventListener("fetch", event => { event.respondWith(handleRequest(event.request))})

The code is self-explanatory. Basically, if the path is myBlog.hostname , it acts as a reverse proxy and fetches example.com/blog .

The other thing it does is fetch the static content from the /blogpath. By default, the ghost should have the following paths for static content.

assetsPathnames: [“/public/”,”/content/”, “/assets/”]

Deploy the code. Done.

Just kidding. This is a ‘how do I get around X” blog post, did you expect it to be easy?

Go back to “Add route” button at Cloudflare Workers page and add the following.

  • *example.com/blog*
  • *example.com/content*
  • *example.com/public*
  • *example.com/assets*

Last Touches

You need to make sure your ghost blog link works with /blog . To do that, change the @site.url argument from blog.example.com to blog.example.com/blog.

If you are using Ghost Pro, you might need to change the routes.yaml to something like

routes:
/signup/: /blog/members/signup
/signin/: /blog/members/signin
/account/: /blog/members/account
collections:
/:
permalink: /blog/post/{slug}/
template: index
/blog/:
permalink: /blog/post/{slug}/
template: index
filter: primary_tag:blog
taxonomies:
tag: /blog/tag/{slug}/
author: /blog/author/{slug}/

Now you have a Ghost blog using Netlify and Cloudflare. Good job.

🎉 Edgecase 🎊

  • You can’t use Netlify CDN. Cloudflare CDN is good or even better. You might need to configure caching rules to make your landing page and blog faster.
  • Netlify invalidates cache when you do git push . You won’t have this ability out of the box anymore. You can use Github Actions to get the same functionality or you can use a Workflow Automation tool like Zappier to achieve this.
  • Rather than Netlify SSL, you can use Cloudflare SSL. Select “Full” in Cloudflare SSL Tab.
  • You won’t be able to use blog.html , content.html , public.html , assets.html in Netlify anymore. You have overwritten those paths. If you have any, delete or rename them.
  • Cloudflare is fast enough. You won’t have any performance issues. Sidenote; Ghost Pro originally uses Cloudflare as CDN.

Lastly, as developers, we want everything to be easy. Tools like Netlify and Ghost gives us out the box solutions. However, once in a while you have generic use cases like this — which they have no good way of solving — you spend hours, days, and even sometimes weeks trying to figure it out. Stop cursing around. You literally deployed a custom blog and static webpage in mere hours! Be grateful for all the tools we have. You did great! Be happy.

If you enjoyed this post, you can follow me on Twitter or reach out to me on thellimist.com.

--

--