Ditching Slow Blog Loads – My Ghost + BunnyCDN Adventure

Ditching Slow Blog Loads – My Ghost + BunnyCDN Adventure
Ditching Slow Blog Loads – My Ghost + BunnyCDN Adventure

Tired of slow-loading pages on your Ghost blog? Probably unlikely because Ghost is super-fast out of the gate but imagine even snappier page loads, happier visitors, and better SEO. Ready to get started? Let's dive into how to integrate Bunny CDN with your Ghost website.


  • Self-hosted Ghost blog installation (Ghost.org)
  • Basic familiarity with nginx configuration
  • Linux-based server environment

Pros and Cons of Going Adapter-Free

Pros of Going Adapter-Free:

  • Simplicity: No additional adapters means less overhead. Fewer moving parts mean fewer potential points of failure and easier maintenance long-term.
  • Flexibility: You get full control over your BunnyCDN configurations directly from their dashboard. This allows for maximum customization for more advanced scenarios.
  • Responsive Images in Ghost: Ghost has a great built-in feature that generates multiple image sizes (based on your theme) when you upload. These size variations let browsers choose the most appropriate image to load depending on the user's device, saving bandwidth and speeding up page loads.
  • Existing Knowledge: If you're already familiar with BunnyCDN's interface, you avoid the learning curve of a new adapter.

Cons of Going Adapter-Free:

  • Manual Setup: Connecting Ghost to BunnyCDN without an adapter means some manual configuration (DNS records, etc.). This can be a bit more intimidating for less technical users.
  • Automation Gap: Adapters often automate tasks like image optimization, cache purging, etc. Without one, you'll either be handling that manually or finding other tools to fill the gap.
  • Responsive Images in Ghost: In order to have responsive images you will need to use Bunny Optimizer or Cloudflare Image Resize functions, which costs extra money.

There are some adapters out there that do support responsive images, but I am not sure exactly how that would work because Ghost themselves state that adapters disable that functionality. Therefore, I am uncertain how you could correct that without altering core files related to this functionality within Ghost.

BunnyCDN | Hop on the Fastest Content Delivery Network!

  • 123Global PoPs
  • 80 Tbps+ Network Capacity
  • 24 ms Avg. Global Latency
  • 40.000+ Satisfied Customers
Start 14-Day FREE Trial

Step 1: Create a CDN Origin

  1. Choose a subdomain: Create a subdomain where your original site content will reside (e.g., nocdn.myghostsite.com).
  2. In your DNS settings: Create a new subdomain and point it to the same IP address as your Ghost blog's main domain. If you're using Cloudflare, make sure to disable the proxy cache for this subdomain.
  3. Configure in nginx: Set up this subdomain as a separate site in your nginx configuration (you can duplicate your existing config and change the domain).
  4. Secure it: Install an SSL certificate for this subdomain (e.g., using Let's Encrypt).
  5. Test: Make sure you can access your Ghost blog at the new subdomain address.

The reason we're using a separate subdomain like nocdn.myghostsite.com as the CDN origin is two-fold:

  1. It allows us to keep the cached, CDN-served content separate from the dynamic, uncached content served directly from your Ghost installation. This separation of concerns makes it easier to manage and reason about the caching setup.
  2. It provides a way to access the original, uncached content from your Ghost installation, which is necessary for cache invalidation and updating the CDN's cache when you publish new content.

By using nocdn.myghostsite.com as the true origin that the CDN pulls from, you ensure your CDN (cdn.myghostsite.com) is serving the latest cached version, while nocdn.myghostsite.com serves as the up-to-date source of truth that the CDN can periodically update its cache from.

Step 2: Set Up Your Bunny CDN Pull Zone

  1. Create an account: Sign up for Bunny CDN at https://bunny.net.
  2. Add a Pull Zone:
    • Give it a descriptive name (e.g., mysite.b-cdn.net).
    • Enter your CDN origin URL (the subdomain you just created).
    • Choose your pricing tier and preferred regions.
  3. Create CNAME: In your DNS settings, create a CNAME record for a new subdomain (e.g., cdn.myghostsite.com) pointing to your Bunny CDN pull zone name.
  4. Enable SSL: Wait for DNS to update, then set up an SSL certificate for your CDN subdomain in the Bunny CDN dashboard.
Setup BunnyCDN Pull Zone
Add Custom Hostname / Linked Hostnames
Enable HTTPS on CDN Hostname

Step 3: Configure nginx

Edit the nginx configuration file for your main site (https://myghostsite.com):

# HTTP settings
http {
    # ... other HTTP settings ...

    # Server block for your main site (www.myghostwebsite.com)
    server {
        listen 80; 
        server_name myghostwebsite.com;

        # ... SSL/TLS settings if you have them ...

location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;

        # Disable gzip between nginx and Ghost for sub_filter
        proxy_set_header Accept-Encoding ""; 

        # Multiple replacements
        sub_filter_once off;

        # Disable gzip for sub_filter
        proxy_set_header Accept-Encoding "";

        # images, including srcset
        sub_filter 'https://myghostwebsite.com/content/images/' 'https://cdn.myghostwebsite.com/content/images/';
        sub_filter '/content/images/' 'https://cdn.myghostwebsite.com/content/images/';

        # poster images are linked as background....
        sub_filter 'background-image:url(/content' 'background-image:url(https://cdn.myghostwebsite.com/content';

        # javascript files
        sub_filter '/assets/js/' 'https://cdn.myghostwebsite.com/assets/js/';

        # public folder test
        sub_filter '/public/' 'https://cdn.myghostwebsite.com/public/';

        # stylesheets
        sub_filter '/assets/css/' 'https://cdn.myghostwebsite.com/assets/css/';

    location ~ /.well-known {
        allow all;

    client_max_body_size 1g;


What's happening here?

  • Talking to Ghost: proxy_pass; is the key! This forwards requests to Ghost, which is running on the same machine (127.0.01) and listening on port 2368.
  • location ~ /.well-known: A special pathway for tools to find important website information.
  • client_max_body_size 1g: Sets a limit on how big a file someone can upload to your site.
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name nocdn.myghostwebsite.com;
    root /var/www/gsp_ghost/system/nginx-root; # Used for acme.sh SSL verification (https://acme.sh)
    ssl_certificate /etc/letsencrypt/live/nocdn.myghostwebsite.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/nocdn.myghostwebsite.com/privkey.pem; # managed by Certbot
    include /etc/nginx/snippets/ssl-params.conf;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;

    location ~ /.well-known {
        allow all;

    client_max_body_size 1g;



Step 4: Verify and Optimize

  1. Build SSL: Run sudo certbot --nginx -d nocdn.myghostwebsite.com to generate a fresh SSL for our nocdn hostname.
  2. Test Config: sudo nginx -t
  3. Restart nginx: Apply your new configuration changes. sudo nginx -s reload
  4. Test with DevTools: Open your blog in a browser, use Developer Tools (Network tab) to confirm images load from the CDN domain.
  5. Cache invalidation: Understand that Bunny caches assets. When you update Ghost content, restart Ghost to force a cache refresh.
  6. Preconnect (optional): Add <link rel="preconnect" href="https://cdn.myghostwebsite.com"> to your site's <head> for faster CDN connections.

CloudFlare w/o BunnyCDN

Aspect Desktop Mobile
Performance 95 75
Accessibility 95 95
Best Practices 96 96
SEO 100 100
FCP (First Contentful Paint) 0.7 s 3.0 s
LCP (Largest Contentful Paint) 1.5 s 3.3 s
TBT (Total Blocking Time) 30 ms 4,560 ms
CLS (Cumulative Layout Shift) 0.001 0.001
SI (Speed Index) 0.7 s 3.8 s

Using BunnyCDN as a classic CDN for images/js/css etc:

Aspect Desktop Mobile
Performance 90 98
Accessibility 100 95
Best Practices 100 100
SEO 100 100
FCP (First Contentful Paint) 0.5 s 2.4 s
LCP (Largest Contentful Paint) 1.1 s 2.8 s
TBT (Total Blocking Time) 0 ms 180 ms
CLS (Cumulative Layout Shift) 0.001 0.001
SI (Speed Index) 0.6 s 2.9 s

Ran 5 CDN tests


Location DNS (ms) Connect (ms) TLS (ms) TTFB (ms)
Frankfurt 3.63 0.75 16.06 75.48
Amsterdam 14.81 3.00 20.48 161.75
London 10.17 3.31 18.28 96.63
New York 13.96 3.00 23.66 71.46
San Francisco 18.90 3.62 22.81 65.92
Singapore 11.70 2.84 15.58 126.24
Sydney 38.23 39.50 135.35 227.78
Bangalore 37.04 133.97 146.63 425.86


Location DNS (ms) Connect (ms) TLS (ms) TTFB (ms)
Frankfurt 1.28 - 12.27 0.68 - 0.87 14.46 - 17.98 27.73 - 148.52
Amsterdam 10.30 - 17.74 2.74 - 3.42 17.81 - 26.68 34.22 - 434.45
London 7.14 - 11.57 2.86 - 3.76 15.42 - 22.65 33.27 - 135.51
New York 6.03 - 23.02 2.30 - 3.64 16.88 - 28.97 43.37 - 124.18
San Francisco 16.05 - 20.31 2.55 - 4.88 21.68 - 25.16 37.18 - 105.92
Singapore 7.30 - 18.85 2.48 - 3.29 13.43 - 18.04 26.80 - 273.25
Sydney 3.57 - 160.81 25.42 - 48.48 123.78 - 148.84 205.37 - 254.75
Bangalore 13.38 - 50.04 129.24 - 139.04 142.38 - 152.89 412.24 - 439.42


Location DNS (ms) Connect (ms) TLS (ms) TTFB (ms)
Frankfurt 1.55 0.73 15.99 30.77
Amsterdam 14.10 2.99 19.24 146.70
London 10.80 3.27 17.44 39.94
New York 13.35 2.91 24.20 60.17
San Francisco 18.57 3.50 22.41 46.10
Singapore 10.84 2.81 15.57 32.21
Sydney 18.99 47.27 135.12 227.66
Bangalore 38.18 132.25 145.32 419.11

Updated BunnyCDN Settings

  • */ghost*  (this keeps your editing area fresh)
  • */members* (so each member sees their own stuff)
  • */sitemap.xml (we don't want an outdated sitemap of your site)
  • */robots.txt (to keep the instructions for search engines up-to-date)

Restricting Origin Access (Optional)

  • Set up an Edge Rule: In Bunny CDN, add an edge rule to send a special header with a secret key for all requests to your CDN subdomain.
  • Configure nginx: In your origin site's nginx config, check for this header and deny requests without it.

Troubleshooting and Support

  • Bunny CDN Documentation: Refer to their guides for specific issues.
  • Ghost Forums: Get help from the Ghost community.
  • My X/Twitter: @graydonschwartz: Contact me directly if you're stuck.

BunnyCDN | Hop on the Fastest Content Delivery Network!

  • 123Global PoPs
  • 80 Tbps+ Network Capacity
  • 24 ms Avg. Global Latency
  • 40.000+ Satisfied Customers
Start 14-Day FREE Trial

Whew, that was a lot of technical details to go through! If you made it this far, give yourself a pat on the back. Setting up a CDN integration can seem daunting, but the performance benefits for your Ghost blog will be well worth the effort.

If you got stuck anywhere along the way, don't hesitate to reach out to the awesome Ghost community forums or even me directly on X/Twitter (@graydonschwartz). We're here to help fellow bloggers elevate their websites.

At the end of the day, a fast-loading blog just provides a better reading experience for your fans and followers. But the real magic comes from your unique voice and the meaningful content you put out into the world. So keep writing from the heart, connect with your audience, and let your authentic self shine through.

Wishing you all the best as you continue growing your Ghost blog! Adventures in coding and optimizations await, but don't forget to also schedule in plenty of breaks for creative inspiration. Happy writing!

I hope this guide makes the process smoother! Let me know if you have any questions.

Read more