Kirby <3 CDN

Performance is not just another buzz word, a fast website is vital to keep your visitors happy. We do everything to make Kirby as fast as possible on the backend, but on the frontend it now also gets easier to deliver images, css and javascript files faster to your users around the world via a CDN.

CDN stands for content distribution network. Without such a CDN all your static files are being served by your web server from a single location. That's all fine until a visitor from the opposite side of the globe tries to open your site. The distance between your server and your visitor leads to a quite massive latency issue for all the assets, which have to be loaded. Even if your site is super fast on your end it might feel horribly slow on your visitor's end.

A CDN takes your assets and stores them on servers at strategic locations around the world. As soon as a visitor comes to your site, the CDN will then find the nearest possible server and load all assets from there. This significantly increases request times and your site will load faster no matter where your visitors come from.

Push and Pull Zones

CDNs usually work with so called push and pull zones.

Push zones

With a push zone, you upload all your assets via FTP or SSH to the CDN's origin server and the CDN will spread them from there for you. This is perfect for static assets that don't change too often or if you have some custom deploy script, which will handle the uploads for you when something changes. You will then get a public URL for your assets from the CDN. Something like

It actually feels a lot like a simple static file server, but with magic superpowers.

Pull zones

A pull zone is much easier to setup. Your original assets stay on your server and must be reachable there. You route every request through the Pull zone, which will then fetch the assets from your server according to the URL.

So for example when your logo is located at

you would simply request the logo from your pull zone like this:

The pull zone would then fetch the logo on the first request and spread it for you across the network. You can define an expiration date for your assets, so the CDN knows when to fetch the logo again.

Lots of new URLs

The push and pull zones are very easy to setup with most CDN providers. But the biggest problem is to change all those URLs on your site to match your shiny new CDN.

On a Kirby site there are three different types of static files, for which we would need to change the URL to point to a CDN.

  • all your javascript files, graphics, fonts and css inside the assets folder
  • all your images, videos, pdfs, etc. inside the content folder
  • generated thumbnails inside the thumbs folder

It would be really painful to do this by hand or create some rewrite rules for it.

Enter: The Kirby CDN plugin

To simplify those steps, I've written a plugin that takes care of modifying the URLs for you:


  1. grab the plugins zip
  2. copy the cdn folder into site/plugins
  3. set your CDN urls in your config:
c::set('cdn.assets', '');
c::set('cdn.content', '');
c::set('cdn.thumbs', '');

Those three options make it possible to use the CDN just for one or two of them, by simply disabling parts with false:

c::set('cdn.assets', '');
c::set('cdn.content', false);
c::set('cdn.thumbs', false);

Especially with lots of changing thumbnails, you must be careful with enabling the CDN option for thumbs. This can lead to quite some storage usage at your CDN provider.

With the plugin and those settings enabled you don't have to change anything in your templates or content files. The URLs for your assets, content files and thumbnails will automatically point to your CDN instead of your own server.


One tricky part about CDNs is the lifespan of your assets on the network. When you upload a new css or javascript file it may take some time until the new file is updated across the network and this could lead to nasty effects.

This is where cache busting is being used to force the CDN to load a new version of your css or js file. The most common way here is to add a version number or timestamp to the filename:

But instead of manually updating the timestamp or version number on each update Kirby has another plugin, which simply adds the modification time of a file to the filename of a css or js file automatically.


The steps to install the cachebuster plugin are very similar to the installation of the CDN plugin.

  1. grab the plugins zip
  2. copy the cachebuster folder into site/plugins
  3. Add the following lines to your htaccess file right below the RewriteBase rule:

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^assets/(. )\.(\\d )\.(js|css|png|jpg|jpeg|gif|ico)$ assets/$1.$3 [L]
  4. activate the cachebuster in your config

    c::set('cachebuster', true);

With those changes the timestamp will be updated, each time you upload a new css or js file and the CDN will automatically pull a new version of it if you use a pull zone.

A huge thank-you to KeyCDN

I have to confess that I skipped CDN integration for Kirby on my todo list for too long. I'm super happy that the friendly folks at KeyCDN offered to support us with free CDN hosting for Kirby and thus finally convinced me to come up with this plugin. The positive effects on are already very noticable.

To be clear, the plugin should work with all CDN providers and is not limited to KeyCDN, but if you are looking for a new one, make sure to give KeyCDN a spin.

This is a companion discussion topic for the original entry at

Even without a cdn the cachebuster plugin is extremely convenient, thanks a lot!

For nginx and the default asset structure the rewrite directive looks like this:

   location /assets/(js|css) {
      if (!-e $request_filename){
         rewrite ^/(.*)\.([\d]+)\.(js|css)$ /$1.$2 break;

Sweet! That cachebusting plugin comes as a great addition to the Kirby arsenal, should even be a core feature. No more manual versioning in the panel. :slight_smile:

Hey, just tried to make use of this plugin. It works totally fine for the normal site but somehow (I guess due to a bad RegEx?) the panel is not receiving any styles or scripts anymore as panel/assets is rewritten to assets/. Any hint what to fix and where?

Solved it. It’s a bit nasty as it looks weird in the config after the change but it works:

in config.php set the CDN URL for assets to the following:
c::set('cdn.assets', '');

in plugins/cdn/cdn.php change the code in line 23 to the following:

$url = preg_replace_callback('!.*?(panel\/)?(assets\/.*)$!', function($match) {
    return c::get('cdn.assets') . $match[1] . $match[2];
}, $url);

This looks if there is a panel in the path and if, adds it to the URI again.

A better way is to just do a if statement there. Insert this code on line 23 and leave the rest stay the way it is.

if(strpos($url, 'panel') !== false) {
  return $url;


The Github links to this plugin are 404. Did it move or is it just gone with the wind? :slight_smile:

The official plugins repo has been moved:

1 Like

Thanks! Documentation on the website needs some polishing up… :wink:

Could you provide a link to where it needs polishing up?


These are the two places I stumbled upon the old url. :slight_smile:

Thanks, @Errrwin. I’ve updated the links in the cookbook recipe, the second one is (currently) a third party site, so I don’t have access to changing that, I’m afraid.

Right. On a fresh Kirby install I installed the CDN plugin and the Cachebuster plugin as per the documentation, but I don’t see anything happening CDN-wise.

Can anyone point me to the right direction? I’ll provide any info needed of course. :slight_smile:



c::set(‘cdn.assets’, ‘’);
c::set(‘cdn.content’, ‘’);
c::set(‘cdn.thumbs’, ‘’);


c::set(‘cachebuster’, true);


# Kirby .htaccess

# rewrite rules

# enable awesome urls. i.e.:
RewriteEngine on

# make sure to set the RewriteBase correctly
# if you are running the site in a subfolder.
# Otherwise links or the entire site will break.
# If your homepage is
# Set the RewriteBase to:
# RewriteBase /mysite

# In some enviroments it’s necessary to
# set the RewriteBase to:
RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+).(\d+).(js|css)$ $1.$3 [L]

# block text files in the content folder from being accessed directly
RewriteRule ^content/(.*).(txt|md|mdown)$ index.php [L]

Where my cdn is (hosted by Keycdn). CDN worked properly with the Wordpress install I had previously, so I doubt that is the part that’s not working. :slight_smile:

Did you rename the downloaded folder to “cdn”?

1 Like

I have now… :unamused:

That is not in the instructions or I totally missed it. Thanks a lot!! :sweat_smile:

Cachebuster needs the seem trick :smiley:

It’s a general rule, the name of the folder must match the name of the main plugin file, otherwise it does not work. The same for the Cachebuster plugin.

(gonna reopen this instead of making a new one-off post)

A general note of confusion:

the cachebuster plugin adds a timestamp using php filemtime to the css / js files. How do you automatise the process of producing a styles.css with that timestamp added in it? There’s no indication on how to change the name of the actual css file automatically.

I wanted to use the plugin in the form of styles.css ?v=timestamp but cachebuster adds the timestamp to the filename directly.

You don’t have to make a file including the timestamp. Look for the rewrite rule on the Cachebuster readme that you need to add to your .htaccess or Nginx config.

File name should stay as it is and that rule will take care of removing the timestamp from the requested filename on the serve so that it can find the right file and return it to the browser.

thank you for the clarification!

I added the rewrite rules in my .htacess file right after

RewriteBase /

then restarted my local php server but the rules are not deleting the timestamp from the files.

(I’m running the php server w/ the latest macOS which comes with PHP 7.1, by running php -S localhost:8000, not sure if this is the cause why a proper restart does not take place)

Any tip?

Generally speaking, it’s not possible for PHP’s built-in webserver to accept .htaccess directives (as it doesn’t rely on Apache), so you’d have to activate & test cachebusting in production - see the docs, section “Multi-environment setup” (for a quick example, check out my StarterKit).

You may activate the cachebuster plugin with c::set('cachebuster', true); inside a production-specific config file, eg (inside site/config).

When using a CDN, be safe & use protection - give kirby-sri a try, which includes cachebusting functionality (or check out the forum post first).