Hosting OpenHTTPd

Goal

This guide will providing web hosting for multiple users on a shared, multi-user server. This setup supports multiple, custom domain names. Afterwards, we will use relayd to provide TLS encryption.

This guide assumes you have read the basic openhttpd configuration guide. This is a slightly more advanced setup.

Configuration

For each user we want to support, we must add two blocks to /etc/httpd.conf. Here's the first block:

server "user.example.com" {
        alias "www.example.org"
        listen on * port 80
        root "/htdocs/user/"
        location "/.well-known/acme-challenge/*" {
                root "/acme"
                request strip 2
        }
}

Line 1 says that this block is for the hostname "user.example.com". On other web servers, this might be known as the virtual host. Replace this with the user's actual subdomain. If the user has an additional domain name he wishes to use, change the alias "www.example.org" to match his actual domain name. For each extra domain name, add a new alias line. If he does not have an alternative name, you can delete the alias "www.example.com" line.

Line 3 tells the web server to listen on all IPs on port 80.

Line 4 tells us that the root directory for all web documents are in /htdocs/user/. Remember, however, that openhttpd runs inside a chroot. All root directories should therefore be prepended with the path /var/www. So, web documents are actually fetched from /var/www/htdocs/user/. If a web browser requests https://user.example.com/index.html, openhttpd would respond with the file /var/www/htdocs/user/index.html.

Lines 5-8 (the location block) is used for requesting certificates using ACME. For any request that begins with http://user.example.com/.well-known/acme-challenge/, openhttpd will look for documents in the new root /acme. Again, openhttpd chroots to /var/www by default, so the document root is actually /var/www/acme/. The directive request strip 2 tells openhttpd to strip off the last two path components from the URL, so that it searches in /var/www/acme/ rather than /var/www/acme/.well-known/acme-challenge/.

Note: You must have a server block listening on port 80. Do not delete this block or else acme-client will not work.

Note: Unlike in /etc/examples/httpd.conf, this guide does not automatically redirect to port 443. Some older web browsers do not support TLS, and we do not want to break backwards compatibility.

Notice that we do not add any blocks with TLS. TLS will be provided by relayd.

Creating Web Files

Next, we create a directory for each web folder, and a symbolic link in each user's home folder that points to that web folder:

$ doas mkdir -p /var/www/htdocs/user/
$ doas ln -s /var/www/htdocs/user/ /home/user/htdocs
$ doas chown -R username:daemon /var/www/htdocs/user/

Again, user with the user's actual username, and user.example.com with the actual subdomain.

Users will now be able to create new web files by putting them in ~/htdocs/.

Start the Server and Test

For the test below, you will want to create an index file in /home/username/htdocs/index.html.

Enable and restart the web server:

$ doas rcctl enable httpd
$ doas rcctl restart httpd

Testing Port 80

To see if the web server is working on port 80, run a test on another computer besides the web server. Here, we use telnet:

$ telnet user.example.com 80
GET /index.html HTTP/1.1
Host: user.example.com

You should a response similar to the one below:

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 34
Content-Type: text/html
Date: Sun, 28 Feb 2021 02:07:55 GMT
Last-Modified: Sat, 27 Feb 2021 10:22:27 GMT
Server: OpenBSD httpd

Troubleshooting

First, stop any active openhttpd processes:

$ doas rcctl stop httpd
$ doas pkill httpd

To check for any configuration errors, run:

$ doas httpd -n

At any time, you can run openhttpd in debug mode:

$ doas httpd -dvv

Firewall

If there were no errors when running openhttpd in debug mode, you may need to check your firewall? to see if it's blocking packets to port 80.

Adding TLS

Next, you'll want to request an SSL cert using acme-client for each domain the user requested.

TLS Acceleration

Finally, you'll want to configure relayd for TLS acceleration.