Setting up a server for Lisp web apps

Table of Contents


This note documents how to set up a basic Lisp web app server.


The main software that Yu'll need is a Lisp interpreter. These instructions assume sbcl (Steel Bank Common Lisp) but other Lisp implementations may work as well. On Debian you can install sbcl using apt:

$ sudo apt-get install sbcl

To run the server on port 80, we'll use the program setcap

$ sudo apt-get install libcap2-bin


The main Lisp libraries we will use are huchentoot and cl-who. These can be installed using a the quicklisp Lisp package manager. According to quicklisp may be installed as follows:

$ curl -O
$ sbcl --load quicklisp.lisp
* (quicklisp-quickstart:install)
* (ql:add-to-init-file)
* (quit)

This installs quicklisp. To install hunchentoot and cl-who, we can go back into sbcl and use quicklisp:

$ sbcl
* (ql:quickload :hunchentoot)
* (ql:quickload :cl-who)
* (quit)

Now we are ready to run our first Lisp web app.

Example code

Here is the source code of hello.lisp:

(require "hunchentoot")
(require "cl-who")
(use-package :cl-who)
(use-package :hunchentoot)

(defclass search-server (acceptor)
    :initform '()
    :accessor dispatch-table
    :documentation "List of dispatch functions")))

(defvar *mysrv* (make-instance 'search-server :port 80))

(defun find-not-nil (l p) 
  (if (endp l)
    (or (funcall p (car l))
         (find-not-nil (cdr l) p))))

(defmethod acceptor-dispatch-request ((srv search-server) (req request))
  (let ((l (find-not-nil (dispatch-table srv)
                         (lambda (disp) (funcall disp req)))))
    (or l (call-next-method))))

(defmacro with-html ((var) &body body)
  `(with-html-output-to-string (,var)

(defun my-prefix-disp (prefix handler)
  (lambda (req)
    (let ((m (mismatch prefix (script-name* req))))
      (if (or (null m) (>= m (length prefix)))
          (funcall handler req)))))

(defun push-dispatcher (srv disp)
  (push disp (dispatch-table srv)))

(let ((counter 0))
  (defun dummy-dispatch (req)
    (with-html (s)
                 "Greetings! You are visitor number "
                 (str (incf counter)))))))

(push-dispatcher *mysrv*
                 (my-prefix-disp "/hello" (quote dummy-dispatch)))

(start *mysrv*)

Next we will see how to run our web app.

Launching the web app

First, we need to allow hunchentoot to listen on port 80. This can be done with the setcap program:

$ sudo setcap 'cap_net_bind_service=+ep' /usr/bin/sbcl

Start the web app as follows:

$ sbcl
* (load "hello.lisp")

You may see a few warnings; these can be ignored.

Testing it out

Launch a web browser and visit http://yoursite/hello. If everything is working, you should see something like this


Something you can play with: Refresh the page a few times and note how the visitor number field is updated.


This logic is implemented inside the web app in the dummy-dispatch function (look for the call to incf there)

Date: 2020-08-03 Mon 00:00