I found a simple solution for offloading images from my self-hosted Ghost and serve them over a CDN for… no money, really. It’s a little bit of a hack, rewriting the image URL’s with nginx, but it works like a dream. We will have three URL’s to keep track of when done…
- www.myblog.com or myblog.com → regular URL for your CDN-accelerated Ghost blog
- nocdn.myblog.com → the Origin for the CDN, your site without CDN-acceleration
- cdn.myblog.com → CDN-cached media URL, a CNAME (alias) of myblog.b-cnd.net
If anyone has suggestions on how to make the set-up even better pls share. Using Ubuntu 22.04, nginx 1.18.0 & Bunny CDN. I am not affiliated with Bunny
Update 25/5: Ghost does not send referrer header when previewing a blog post, if you have Hotlinking protection enabled on Bunny CDN images will not display in Preview.
I also added how to restrict access to our Origin URL nocdn.myblog.com to Bunny in the end of this post.
Also published this as a (CDN-accelerated) blog post with additional info re caching
1. Create a CDN origin URL
That’s your Ghost blog without CDN acceleration. The CDN will not be able to fetch images from your regular blog URL when enabled - the image links are pointing back to the CDN and Bunny will be unable to pull and cache the images. Angry Bunny in a loop, not good.
- Create a subdomain nocache.myblog.com or non-guessable g6b4dp.myblog.com .
- Enable this site in nginx - copy existing .conf and change the server directive to nocache.myblog.com
- Reload nginx and have Certbot generate a SSL certificate for nocache.myblog.com
- Verify that you can access your Ghost blog at nocache.myblog.com
2. Set up an account with Bunny CDN at bunny.net and create a pull zone.
- Under CDN chose Add Pull Zone
- Pull Zone Name: myblog.b-cdn.net - images will be available over CDN from this domain, choose any name.
- Enter the Ghost blog Origin URL created in step 1, ex. nocache.myblog.com
- Choose Standard Tier and disable Zones you have no visitors from.
- Done. Bunny now pulls all html, scripts, css and images from your Ghost blog.
- Create a sub-domain for your CDN-cached images, such as cdn.myblog.com and point this with a CNAME to myblog.b-cdn.net - very elegant.
- Wait for DNS-propagation and make Bunny create a SSL cert for this subdomain.
- Verify that images from your Ghost blog are available on Bunny CDN
- https://myblog.com/content/images/carrot.jpg - Local
- https://cdn.myblog.com/content/images/carrot.jpg - CDN delivery
3. Configure nginx
-
Edit the ‘live’ configuration file for https://myblog.com - not the origin nocache.myblog.com -
-
I use nginx sub_filter to rewrite image-requests to our CDN. It’s basically search-and-replace
sub_filter 'replace this' 'with this';
-
Add this in the nginx proxy_pass block - change myblog.com to your domain and cdn.myblog.com to the CNAME subdomain
# Enable multiple replacements in one request sub_filter_once off; # sub_filter does not work with gzip compression, so disable. proxy_set_header Accept-Encoding ""; # Replace all image links to /content/images with CDN-url sub_filter 'https://myblog.com/content/images/' 'https://cdn.myblog.com/content/images/'; # The same for poster images loaded as background sub_filter 'background-image:url(/content' 'background-image:url(https://cdn.myblog.com/content'; # Optional: javascript libraries sub_filter '/assets/js/' 'https://cdn.myblog.com/assets/js/';
Done. Restart nginx. Verify in Developer Console / Network with a shift-reload in browser.
Restricting access to origin
We only want Bunny to be able to access pull assets from nocdn.myblog.com, so we send a header X-Pull in our requests, and have nginx check for this header.
1. Create new Edge rule
Create a new Edge rule in BunnyCDN control panel
- Action: Set Request Header
-
Header: Name:
X-Pull
-
Header Value:
random12345
- use any passphrase here -
Conditions: Request URL matching Any for
cdn.myblog.com/*
2. nginx config for nocdn.myblog.com origin URL/host
Add this to the proxy_pass
block in nginx. If the header X-Pull in the request does not contain the key we assigned in the Edge rule we return 403 Forbidden status code.
if ($http_x_pull != "random12345") {
return 403;
}
This solution was suggested by KeyCDN and they have a HTTP Header Checker to verify that requests with X-Pull are accepted.
PageSpeed with Bunny CDN enabled is 97, pretty good…
/magnus