Build Status Coverage StatusQuicklisp

The static asset middleware is for handling versioned static assets. That means that, when the assets of your webapp change, it will be referred to by a different filename, allowing a browser to instantly know that it needs to download a new file. Assets are served with the maximum cache time headers set so browsers will never ask for them again.

It comes with the clack-static-asset-djula-helpers package which extends the djula templating language to allow inserting cache busted urls for your files easily.


Use by wrapping your clack app in clack-static-asset-middleware:*clack-static-asset-middleware*.

(funcall clack-static-asset-middleware:*clack-static-asset-middleware*
              :root (asdf:system-relative-pathname :webapp #p"static/")
              :path "static/")

It can be configured by specifying:

  • path: the path on your app that you want your assets to be served from. That is{path}/images/some-sheep_12..532.png.
  • root: The root directory that your assets reside in on the filesystem.
  • cache-buster-function: A function of two arguments (pathname cache-string) that generates an appropriate, cache-busted name for a file. The default creates filenames like images/some-sheep_12421345423fea543265cf43226512a6.png.
  • cache-unbuster-function: A function that takes a cache-busted url and resolves it to the un-busted filename.
  • filter-function: A function that identifies files that should not be served but are in the root directory anyway. It acceps a pathname and return a boolean where t means 'do not serve' and nil means the file is good to serve. By default, it blocks files beginnig with a ., which would be hidden on a UNIX filesystem.

Once you have done that, you can call busted-uri-for-path to convert a relative path on your system into an appropriate cache busted url suitable for inserting into templating and showing your friends. There is a helper package for accessing this functionality within the Djula templating language below.

Real-World Usage

This middleware is not optimized to serve files. It should not be particularly slow, but it was not designed to be your CDN. In real-world situations, you should consider putting a CDN or nginx proxy in front of your app for best performance. I have not done this, yet, but it's probably something I should work on.

Ideally, you might set this up behind an nginx reverse proxy and let it handle un-busting your URLs like so:

server {
  # ...

  location ~* ^/<YOUR PATH HERE>/(\w+)/([^/]+)_\d+\.(js|css|png|jpg|jpeg|gif|ico)$ {
    alias <YOUR ROOT HERE>/$1/$2.$3;
    add_header Vary Accept-Encoding;
    expires max;

  # ...

Example taken from the blog of Ben Ripkens.


clack-static-asset-middleware is not yet in quicklisp, so clone it into your quicklisp local-projects directory. Then, run

(ql:quickload :clack-static-asset-middleware)

or refer to it in your system definition

(asdf:defsystem my-great-webapp
    :depends-on (#:clack-static-asset-middleware)

Template Helpers

Right now, there is a helper package, clack-static-asset-djula-helpers, which provides extensions to the Djula templating language for inserting busted URLs into templates. Once you've loaded the system, there will be two new djula tags available.

  • asset-path: Inserts a busted url to the given path. <img src="{% asset-path "images/gustywinds.jpg" %}" /> => <img src="/static/images/gustywinds_423534...a3.jpg" />
  • stylesheet-tag: Inserts a busted url conveniently inside a stylesheet tag. {% stylesheet-tag "styles/cool.css" %} => <link rel="stylesheet" href="/static/styles/cool_a05b6...878f.css">


  • Matt Novenstern (

Copyright (c) 2016 Matt Novenstern (


Licensed under the MIT License.