Minimal webserver setup with thttpd and stunnel on OpenBSD

Introduction

OpenBSD's httpd is wonderful but it doesn't serve utf8 or at least I couldn't find the option to enable such functionality. I want to be able to serve web pages with special symbols and such so I've been looking for a minimal program that'll let me do that and I've settled for thttpd. It is a very straightforward program and requires no configuration files like the ones used by nginx, apache or httpd. You should really think of more like using cat or some other basic utility. The configuration file that it does have just mirrors the command line arguments you can give it. Some of the features you might usually want to enable or disable in a configuration file are handled with file permissions and putting stuff in different directories, namely auto-indexing and virtual hosts. This approach does have some limitations though, thttpd does not have support for ssl so if you want to use different certificates for different subdomains, it will be a little tricky to say the least. If you just want to serve a static website though, the approach that I will describe bellow is be more than enough.

Update: It turns out you can specify the charset from within html so that's why httpd was missing the option. But it can also be specified from http itself so that's why thttpd had the option. All that is to say that http is a shit protocol and that you can include the following inside <head> to specify the charset:

<meta charset="utf-8"/>

Preparing the website directory

Make files world readable. Instead of a special user and group, thttpd looks at the "other" field permissions.

find /path/to/local/webdir/ -type f -exec chmod o=r {} +

Enable viewing of the contents of directories (o=x is not enough).

find /path/to/local/webdir/ -type d -exec chmod +x  {} +

I'm lazy so my local webdir is also my working directory. This means that I'll often have unfinished files left in there. I don't want them to be visible to all so I usually disable auto-indexing.

find /path/to/local/webdir/ -type d -exec chmod o-r {} +

(For those of you wanting to read the source of the html pages or pdfs you are seeing, you can change the url from something.html to something.md or something.ms.)

Then copy the prepared website directory to the server, I use rsync.

rsync -rlpv /path/to/local/webdir server:/var/www/htdocs/yotsev.xyz

You can use scp instead but it won't preserve file permissions so you would have to run the commands above after every copy to the server.

Note: if you want ssl, you still have some work to do here. See the SSL section.

thttpd configuration

Append the following lint to /etc/rc.conf.local. The -dd option is the data directory aka the current directory after chroot (-r) aka the remote webdir.

thttpd_flags="-r -T utf8 -dd './htdocs/yotsev.xyz/'"

Now, to enable and start the daemon

rcctl enable thttpd
rcctl start thttpd

SSL

Unfortunately thttpd doesn't support ssl so we'll have to use stunnel. I have only changed the following lines from the default configuration file in /etc/stunnel/stunnel.conf. Be sure to change the certificate paths to your domain name.

;[pop3s]
;accept  = 995
;connect = 110
;cert = /etc/stunnel/stunnel.pem

;[imaps]
;accept  = 993
;connect = 143
;cert = /etc/stunnel/stunnel.pem

;[ssmtp]
;accept  = 465
;connect = 25
;cert = /etc/stunnel/stunnel.pem

; TLS front-end to a web server
[https]
accept  = 443
connect = 80
cert = /etc/ssl/yotsev.xyz.crt
key = /etc/ssl/private/yotsev.xyz.key

Since thttpd doesn't have a special configuration file to specify the location of acme challenges, we'll just have to create a symbolic link in the file system. This can be easily done for acme-client by running:

mkdir /path/to/local/webdir/.well-known
cd /path/to/local/webdir/.well-known
ln -s /acme/ acme-challenge

Be sure to check if .well-known has the correct file permissions. The above command assumes that thttpd will run with chroot in /var/www/.

Then enable and start stunnel

rcctl enable stunnel
rcctl start stunnel

That's it.

Notes

If you have any questions or want to do something differently, read the manual. Most things are explained there.