darkhttpd with stunnel


A minimal web server with authentication for serving static content

Some weeks ago, I booked a virtual private server (VPS) for playing around with web servers. I don't want to run anything that needs a high performance, so I bought the smallest solution available: 1 virtual core, 512 MB RAM, 10 GB SSD (cost: 1 € per month). This comes with Plesk Obsidian, Apache and nginx preconfigured but I really wanted to start from scratch with a minimal (suckless) solution.

My aim was to serve a static version of my personal TiddlyWiki with some sort of authentication.

Minimal HTTP servers

I have considered these servers:

All of them consist of roughly 2000-3000 lines of C code (don't know about busybox) and offer similar features. Here is a quick comparison:

drop privileges
dir. listing✔²✔²
(basic) auth.✔⁴
virtual hosts

How to serve static content via HTTP (Port 80)

I have put my web content in /srv/static. This folder belongs to root:root. And I have created a user www (group www) for serving the content.

┌──────┐           │ port 80 (http)
│web   ├───────────────────────────
│server│           │
└──────┘ localhost │ internet
sudo busybox httpd -h /srv/static -p 80 -f
sudo quark -d /srv/static -p 80 -l
sudo darkhttpd /srv/static/ --port 80 --chroot --uid www --gid www
sudo althttpd --root /srv/static/ --port 80 --user www

(You need superuser rights for running a server on port 80 and also for using the chroot feature.)

I picked darkhttpd because I want to have directory listings and use basic authentication.

Adding HTTPS

When you want to use basic authentication, you need HTTPS. Otherwise you'll send your password unencrypted (though base64-encoded) over the internet.

Most tiny servers don't offer HTTPS built-in. They are meant to be used with stunnel or something similar for getting TLS encryption (HTTPS):

quark does not natively support TLS. A more suckless approach than to implement TLS into it is to use a TLS reverse proxy (e.g. tlstunnel, hitch or stunnel). It accepts encrypted TLS connections and forwards them as unencrypted requests to a server. In this case, one can run such a reverse proxy to listen on a public IP address and forward the requests to a local port or UNIX-domain socket.

(Source: https://tools.suckless.org/quark/)

I picked stunnel because it seems to be the most mature solution and it's referenced in this busybox wiki page and in the althttpd README.

How to configure stunnel + darkhttpd


  1. Install stunnel:
# on Ubuntu
sudo apt install stunnel4
# generic
cd ~/Downloads
wget https://www.stunnel.org/downloads/stunnel-5.60.tar.gz
tar xvzf stunnel-5.60.tar.gz
cd stunnel-5.60/
sudo make install
  1. Get a SSL certificate:

    • For example via acme.sh (follow instructions in the README).
  2. The following examples assume you have put your certificate files here:

    • /etc/acme.sh/static/cert.pem
    • /etc/acme.sh/static/key.pem

Let's consider three scenarios:

1. http and https

In case you don't want to use basic authentication (or if you do, make sure not logging in while connected via port 80):

┌──────┐           port 80 (http)
│web   ├┬──────────────────────────
         └───────┘ port 443 (https)
  1. Start darkhttpd on port 80 (see above).
  2. Configure stunnel like that:


output       = /usr/local/var/log/stunnel.log
cert         = /etc/acme.sh/static/cert.pem
key          = /etc/acme.sh/static/key.pem

client       = no
accept       = 443
connect      = 80
  1. Start stunnel: stunnel

2. https only, with basic authentication

┌──────┐  port 10080 (http),
│web   ├┬──────── localhost │ internet
│server││┌───────┐          │
         └───────┘ port 443 (https)
  1. Start darkhttpd with basic authentication, only accessible from localhost:
sudo darkhttpd /srv/static/ --port 10080 --chroot --uid www --gid www --addr --auth john:"$PASSWORD"

(Could also be started without sudo and --chroot since we use a port number > 1024.)

  1. Configure stunnel:


output       = /usr/local/var/log/stunnel.log
cert         = /etc/acme.sh/static/cert.pem
key          = /etc/acme.sh/static/key.pem

client       = no
accept       = 443
connect      =

3. https plus a redirection to https on the http port

Follow steps from 2. https only, with basic authentication.

Additionally start another darkhttpd instance which redirects HTTP requests on port 80:

sudo darkhttpd /srv/static/ --port 80 --chroot --uid www --gid www --forward-all https://yourdomain.info