Hot-reloading with busybox httpd

I’ve been recently working on a simple static website1. I quickly noticed previewing it by browsing index.html is far from perfect. I needed a local server to host it. Ubuntu comes with busybox httpd by default so I decided to give it a go.

The server can host files form the current dir (-f makes it run in foreground rather than as a daemon):

busybox httpd -f -p 8090

It worked great until I got tired with hitting F5 to reload the page. I needed hot-reloading.

After some trial and error I have come up with a solution.

Once the page has loaded it should make an async request which would block in the background until a file system change is detected. The request’s call-back would simply call location.reload() which would repeat the whole process. The only option to implement server-side scripts in busybox httpd is CGI. We can use a CGI script to monitor the website dir for changes.

The below script blocks until a file change is detected2:

#!/bin/sh

watch -d -t -g -n 1 "ls -lR --full-time ../ | sha1sum"

echo "Content-Type: text/html"
echo ""
echo "OK"

The above script generates a SHA1 checksum of the file list in the parent dir (recursively) every second (the -n switch). When the checksum changes (i.e. there was a file change) it exits (the -g switch) and sends a success response to the client.

busybox httpd detects CGI scripts the cgi-bin dir located in the www root dir so I saved the above script at cgi-bin/reload.sh

The below script reloads the page when the /cgi-bin/reload.sh call returns:

<script type="text/javascript">
    fetch("/cgi-bin/reload.sh").then(() => location.reload());
</script>

Obviously we don’t want hot-reloading to be present in production. It would be great if we could only inject the client-side script in the development scenario. For that I used another CGI script. busybox httpd can load the home page from /cgi-bin/index.cgi given there is no index.html in the www root and the script has 700 permissions. I renamed my index.html to index.htm and then made a /cgi-bin/index.cgi script that reads the contents of index.htm, injects the script, and returns the output to the client.

The index.cgi script:

#!/bin/bash

echo "Content-Type: text/html"
echo ""

index=$(cat ../index.htm)
inject='
<script type="text/javascript">
    fetch("/cgi-bin/reload.sh").then(() => location.reload());
</script>'

echo -e "${index/<\/title>/"</title>\n    $inject"}" 

And that’s it - a nice low-ceremony hack for hot-reloading without additional dependencies.


  1. Just HTML and CSS - no build, no pre-processors, no package managers etc. ↩︎

  2. One could also use inotifywait but it requires installing an additional package. ↩︎