StaticBuilder - Kirby as a static site generator


Not writing into content folder, ‘just’ adding this missing files in the static export folder(s).



I’m not 100% sure what’s happening here. My understanding is that:

  1. You have a multilingual site, with a default language.
  2. And some pages which are only available in another language (not the default language).
  3. The default filter in StaticBuilder looks if the the $page->textfile() exists. In multilingual sites, $page->textfile() returns a theoretical filename like e.g. content/mypage/mytemplate.xx.txt, where the xx part is the default language code and mytemplate is either the base name of the text file that was actually found by Kirby in the first place, or the folder name (e.g. content/mypage/mypage.xx.txt).

That filter was actually made to avoid building pages that are actually just a folder with some assets or data. For instance if you put an image gallery in a page’s subfolder and don’t want that folder to be built as an HTML page using the default template. But maybe that’s an overly specific need, or maybe $page->textfile() is a not-so-reliable tool to do that.

To answer your suggestion: I’m not so keen on changing the default filter in backwards-incompatible ways. You can always use your own filter function. This is documented here:


no, all pages with just the default language (de in my case) are skipped. if i never switch to “en” in the panel and save the page there, there will only one file:… and this is skipped by staticbuilder.


That’s strange.

Anyway, I changed the technique for trying to determine if a page has a content file or not. (The built-in methods in Kirby don’t seem to do that, or there is a bug.) It should be more reliable now.



Now it works as expected! Only the module-templates are skipped. Great! <3

Thank you very much!


It seems like language strings get stripped:

<a href=""><?php echo l::get('education-home'); ?></a>


<a href=""></a>

Any ideas?


@PaulMorel Looks like a bug.
I opened an issue, could you try the workaround mentioned in it?



how can i copy a special files to the static root? In my case: a special .htaccess file.
Is this possible with a config setting?

Another question: after exporting a static version of my site some background images are not visible.
I use the modules plugin and on the non static site it looks like this (working):

background-image: url(<?= $module->url().'/'.$module->bgimage(); ?>);


background-image: url(http://localhost/kirby/site/products/columns-ix4gsq/gilles-lambert-mobile-call-thin.jpg);

Exported it looks like this (not working):

background-image: url(./columns-ix4gsq/gilles-lambert-mobile-call-thin.jpg);

$module->bgimage() is a image field and i get the right file name if i echo this.
But how i get the URL working in the static version?

if i use (image:gilles-lambert-mobile-call-thin.jpg) in the module text the URL is correct:

<img src="http://localhost/kirby/site/content/3-products/9-columns-ix4gsq/gilles-lambert-mobile-call-thin.jpg" alt="">

How does the Image tag get the right URL? Can’t find it anywhere.

Any ideas?


Kirby Modules Plugin

Yes, see the docs on GitHub.

In your case, it might look like:

c::set('staticbuilder.assets', [
    'assets' => 'assets',
    'thumbs' => 'thumbs',
    'site/static-template/favicon.ico' => 'favicon.ico',
    'site/static-template/.htaccess-template' => '.htaccess'

(Just an example, the source paths can be anything you want, and the destination paths are the paths/filenames to use inside of the static folder.)

You should make sure that the image is available in the static build (the contents of the static folder should be self-sufficient), then provide a URL that works.

In this specific case, since the site folder is not copied to the static folder (and should not be!), even if the $module->url() resolved to something sensible (no idea why it doesn’t here), that would not be very helpful since you’re not copying the site directory or even the module’s directory (which probably contains some PHP you should not copy either).

If it’s just images, my recommendation would be to make thumbs of the image so they end up — even as a pure copy with no image processing — in the thumbs folder. Something like:

background-image: url("<?= $module->bgimage()->thumb()->url(); ?>");

If you have other resource you’re calling directly, be them JS or CSS or documents, I’m not sure what the right solutions would be. You can list specific files individually in the 'staticbuilder.assets', but if you have many it’s going to be tedious and not future-proof.

By the way, in this example I’m supposing that $module->bgimage() is an image object, but if it’s a field where you wrote the filename of an image, you probably need to do something like this:

<?php if ($image = $module->bgimage()->toFile()): ?>
  background-image: url("<?= $image->thumb()->url() ?>");
<?php endif ?>


thank you! helped a little bit but i have still issues with the url to thumbs:

I do not get a URL to the thumbs folder:

$myimage = new Asset($img);
thumb($myimage, array(), false);

gives always a URL like localhost/kirby/site/home/slider-a4xiyr/reflist-greentech.png. This works on the original site but fails after static export.

$myimage = new Asset($img);

also does not give a URL to the thumbs folder.



Now i got a link to the thumbs folder:

$myimage = new Asset($tag->file($img));

but only if $img is a full path to the image like: home/slider-a4xiyr/reflist-alpenbank.gif
it seems this is due the path resolving issue in the actual kirby version.



i use a route to get a link to my sitemap:

c::set('routes', array(
    /* */
        'pattern' => 'sitemap.xml',
        'action'  => function() {
            return site()->visit('sitemap');
        'pattern' => 'sitemap',
        'action'  => function() {
            return go('sitemap.xml');



$ignore = array('sitemap', 'error', 'search', 'demo');

// send the right header
header('Content-type: text/xml; charset="utf-8"');

// echo the doctype
echo '<?xml version="1.0" encoding="utf-8"?>';

<urlset xmlns="">
  <?php foreach($pages->index() as $p): ?>
  <?php if( in_array($p->uri(), $ignore) || substr( $p->intendedTemplate(), 0, 6 ) === 'module' ) continue ?>
    <loc><?php echo html($p->url()) ?></loc>
    <lastmod><?php echo $p->modified('c') ?></lastmod>
    <priority><?php echo ($p->isHomePage()) ? 1 : number_format(0.5/$p->depth(), 1) ?></priority>
  <?php endforeach ?>

Is there any way to generate the static sitemap.xml in the document root with the builder or do i need something self made (maybe in the deploy script)?



Currently there is no support for routes, or for Kirby Content Representations (which are a bit similar to routes).

What you could do, though, is to rename your page from sitemap to sitemap.xml. Not sure if the Panel lets you do that, but on the filesystem side there should be no problem. For instance on a project I have a page whose folder is named searchdata.json, that’s not a problem at all.

The result will be that your page’s URL will already contain an extension, so StaticBuilder won’t try to add a .html extension (or /index.html if you’re using that). The resulting file will be static/sitemap.xml.

Note that says extensions in URLs don’t work, but in my experience they do and I made a project in 2016 – updated recently to latest stable Kirby – which uses that. Not sure what that issue is about. Maybe that the Panel doesn’t let you input a page slug with a dot or something like that. But if you create the page’s folder manually, nothing stops you from naming it sitemap.xml (just checked that).

Once you do that, remove your two routes (since they’re now irrelevant), and add a header function for your sitemap:

c::set('headers' [
  /* Executed for pages using the sitemap template, but ignored by StaticBuilder */
  'sitemap' => function() {
    header('Content-type: text/xml; charset="utf-8"');

And remove the corresponding header(...) line from your template, because it could break the static build.

For the record, this is documented here: Best practices for static sites.


Whow! It works and i have a static sitemap.xml now.

Thanks a lot!




is it possible to load a different config file for the static export?
The dynamic page runs at localhost but must be ready for after export.

any ideas?



Isn’t this handled by default?
See: Kirby StaticBuilder options > Hosting on a domain or subdomain.

With default settings your page URLs should look like this in the HTML:

<a href="/some/page">Some Page</a>

If you configure the MultiViews option (for Apache, or a similar option for other webserver software) you should get the some/page.html file.

But if you want to have the full domain in the HTML attributes for some reason, you can always declare your base URL explicitely:

c::set('staticbuilder.baseurl' => '');

This will prevent you from checking that your site is built correctly by navigating between pages in your local static folder, though, since each link will send you to the live website.


Of course this does the trick.

But my usecase here is a little tricky: i have to export the site to something like a stating/testing server without the final URL but the URL of the testing server. After that the static site should be deployed to a live server.

Anyway…just ‘loud thinking’ by me.
It seems this is something you can’t solve in your plugin. so no problem… for you.

Thank you for the great plugin!



Doesn’t c::set('staticbuilder.baseurl' => '/'); (the default) work for both the staging and the production servers?


Yes, removing c::set('url... and set c::set('staticbuilder.baseurl' => '/'); seems to do the trick.
Thanks a lot!



@fvsch: Is it possible to check (on a template/module…) if a static export is running?
Or start a static export as a not logged in user? I have some frontend snippets only
visible to editors. They should not be exported to static.



I have tested the new version. Thank you, it runs very good, after I have added to the working config.php:

c::set('staticbuilder.filter', function($page){
    if ($page->isVisible()) {
        return true;
    } else {
        return [false, "Invisible page '$page' excluded from static build"];

But how can I avoid to copy the *.txt and *.md files within the /content directory to the static files?