RSS Feed

Lisp Project of the Day


You can support this project by donating at:

Donate using PatreonDonate using Liberapay

Or see the list of project sponsors.


This middleware in some sense like a builtin lack-middleware-static, reviewed last week.

The difference is that this middleware is more suitable for production because implements an infinite cache TTL for static assets.

An infinite cache TTL trick is useful when you want to speed up your website loading. Here is how it works.

Your server returns Cache-Control HTTP header and set static files TTL to some large value like a year to make it cached for a long long period of time.

But how to expire this cache if you will need to update CSS or JavaScript on your site?

The only way is to change the URL. This is what lack-middleware-static does for you. It calculates MD5 hash from the file's content and makes it a part of the URL.

When the content of the static file is changed, its URL changed as well. Browser notices that change and reloads the content.

Middleware provides a special tag for Djula template language. Setting up templates for djula is out of thescope of this post and we'll use busted-uri-for-path instead, to create a path to a file including a cache hash.

First, we need to start our server and configure the middleware. Pay attention to the probe-file call. Root should be an absolute pathname. With relative pathname, you'll get a wrong result :(

POFTHEDAY> (clack:clackup
             (lambda (env)
               (list 200 (list :content-type "text/plain")
                     (list (format nil
                                   "Access this file: ~A"

             :path "static/"
             :root (probe-file "static-files/"))
            :port 9004)
Hunchentoot server is started.
Listening on

Now we can access our index page to get the static's URL:

POFTHEDAY> (dex:get "http://localhost:9004/")
"Access this file: /static/site_ebb4fccbf8e0590b0fcf44c3748af88d.css"

Pay attention to the file's suffix. It is an md5 hash from file's content. This sum is calculated when you start the application. If you'll change the file, during the next deploy another md5 hash will be generated and browser will reload its content.

If we'll access this file, the server will respond with Cache-Control header and set the TTL to 1 year:

POFTHEDAY> (dex:get "http://localhost:9004/static/site_ebb4fccbf8e0590b0fcf44c3748af88d.css")
"body {font-size: 10px;}"
200 (8 bits, #xC8, #o310, #b11001000)
#<QURI.URI.HTTP:URI-HTTP http://localhost:9004/static/site_ebb4fccbf8e0590b0fcf44c3748af88d.css>
#<SB-SYS:FD-STREAM for "socket, peer:" {1001E37873}>

POFTHEDAY> (rutils:hash-table-to-alist #v56:2)
(("date" . "Tue, 30 Jun 2020 19:39:55 GMT")
 ("server" . "Hunchentoot 1.3.0")
 ("accept-ranges" . "bytes")
 ("last-modified" . "Tue, 30 Jun 2020 19:15:56 GMT")
 ("vary" . "Accept-Encoding")
 ("cache-control" . "public, max-age=31556926")
 ("content-length" . "23")
 ("content-type" . "text/css; charset=utf-8"))

Tomorrow we'll review the last Clack's middleware. I found only 3 of them on the Quicklisp. If you know about other middlewares, let me know and we'll continue our journey to the world of web development with Clack!

Brought to you by 40Ants under Creative Commons License