Increase the performance of your self-hosted ghost blog massively with proper caching in Cloudflare

I found out a good way to cache ghost blog with cloudflare using cloudflare workers.
We can implement full page cache for logged out users. And bypass dynamic contents caching for logged in user. It makes your self-hosted blog highly scalable. (If you have maximum logged in user then it is not a good idea). But otherwise it is working fine for me. I even load tested my blog.


Interesting stuff, Shounak.

What happens to the advertisement scripts?

Works fine for me, both ad units and auto ads. Because ads are always dynamically loaded from google. I don’t know about the scenario where you want to disable ads for paid tier subscribers though.

Thanks for your tutorial @smartgoat . It’s work perfectly!
Only one thing:
Configure this is not necessary?

When I am not logged in, the cache shows “expired”.

Captura de pantalla 2023-12-16 a las 12.31.12

I don’t use this for my site. It this probably caching your site in your origin server for 1day. But in my workers script I am caching for 14 days in the edge. So edge is serving the html where the cache header says that the cache is expired in origin. That is what is happening here probably. As I don’t have this code I get max age 0. I also don’t cache it in browser because you can’t force clear browser cache for users, but can purge edge cache easily.

1 Like

Thanks @smartgoat ,
It is very strange. The static files mark me HIT, but in the Homepage or articles pages: Expired.
Don’t you have anything else configured in CloudFlare? Page Rules, Caching… or just the Worker?
It’s very strange then…

In this case, Can you check that the Worker code you have published on your website is correct?
I say this because: On your site: ghost / returns me DYNAMIC (It’s OK). But in my site ghost / it returns HIT.
It’s like it’s upside down.

1 Like

Thank you for pointing that out. I missed that, very very sorry for it. In the blog basic cache I talked about urls to bypass, turns out, that first point it is actually necessary. I checked using cloudflare tools and surely /ghost/ is hitting page rule I have setup. Page rule is not needed for cache, cache rule is.

1 Like

This is the tool from cloudflare.
I will update my post within 30 minutes with the new information.
Thanks again for pointing that out @joan

Thanks for the update @smartgoat
Now, in /ghost/ show “DYNAMIC”. Now it’s OK!
But on Homepage and articles stills shows “EXPIRED”. Look: HDS+

Can you check your Worker code that everything is ok too? The only thing I have changed is my URL:

if (
    url.pathname.startsWith('') ||
    url.pathname.startsWith('') ||
    url.pathname === '/sitemap.xml'

I checked your website and mine using this tool Measure TTFB from 40 Locations - SpeedVitals
and for you Measure TTFB from 40 Locations - SpeedVitals
I saw some of your cache are expired some missed.
I copy & pasted the live workers code in my blog, so the code is fine.
I want you to check two things:

  1. In caching > Tiered cache > smart cache topology is turned on.
  2. Or probably due to the code you inserted in your config,prod.js.

But i am not sure what is going on in your case. It definitly caches, but also misses. also i saw errors in the test from India and UAE. I told you my setup exactly. I am not sure what is going on with your setup. Mine is on cloudflare free plan.

1 Like

OMG! Now yes @smartgoat Can you check it please too?

I had deactivated smart cache topology.
I just activated it and it’s all HIT !


1 Like

One question @smartgoat

Is it possible to always bypass Ghost member cookies in your script?

With your script, unlogged users, it does cache everityng. This causes that sometimes, it does not show the “login / registration” correctly in some parts of the site.

Can you please elaborate?
Not sure about this fix.
I checked the workers log.
We probably need to add a bypass rule to bypass url starts with

While at route page, go to ‘request-limit-failure’ and set it to Fail open. So that if workers limit is exhausted it does not block traffic to your site.

I use Cloudflare, and I achieve better results without using Workers (which is limited in the free plan).

I just added the website to it by pointing the nameservers to it and then Rules > Page Rules >* Browser Cache TTL: an hour, Cache Level: Cache Everything, Edge Cache TTL: 7 days. In this way there is no 100k/month limitation…

Also, I see you are suggesting Digital Ocean in your blog, but there are far better, managed and cheaper options for self hosting Ghost, like Pikapods.


Hi, Thanks for reading my blog. but you have missed a few points.

  1. This is specifically used if you have your members features enabled, which means member signup etc.

  2. I have mentioned in a alert box in my post your method too, for the sites which does not have members enabled.

  3. I actually suggest hosting ghost with CloudPanel in any vps, I use oracle free tier with CloudPanel and host 2 Ghost Blogs, 1 reverse proxy on the same VPS. I have tutorial for that. in my blog. I placed digital ocean too because they have a one click deploy option.

Thanks for the reply.

I have to add: URI FULL starts with… in Cache rules?

And in your worker script too, right?

if (
url.pathname.startsWith(‘https://xxxxxx/ghost/’) ||
url.pathname.startsWith(‘https://xxxxxx/p/’) ||
url.pathname.startsWith(‘https://xxxxx/members/api/member/’) ||
url.pathname === ‘/sitemap.xml’

It’s strange, but only sometimes, everything that has to do with Ghost Api members disappears (Menu, login / search bar…) And also only in some parts / articles of the site. It’s very estrange.

You probably won’t need to add it to workers, only cache rules. But returning your error. In your worker dashboard you will see the request graph, where you may find error rate. If there is errors, the run log stream and try to reproduce the error in another Device, keeping an eye on Your log stream. You may find the error and be able to debug.

Hi @smartgoat,
I just sent you a private message with a video to show you what happens. With the Worker ON, some sections/elements of Ghost are not displayed in some parts of the site.

Yes, I noticed that too but with navigation, but i was not sure what was causing the problem, because I also added cloudfront to deliver my css js and some images. Clearing cache solves the issue. I just disabled cloudflare rocket loader and minifying css js and html. I am currently monitoring the result. Can you please also disable rocket-loader and auto minify and check the results?

btw: i checked and it showed to me. and you are probably using workers, because cache was hit.

1 Like