Basic OpenHTTPd Configuration

OpenHTTPd is a light-weight web server developed by the OpenBSD dev team.

Overview

Pros:

  1. Lean: Small, no plugins
  2. Clean code
  3. Secure: Strict validity checking, privilege separation, strong cryptography
  4. Fast
  5. Easy to configure with good manpage documentation

Docs and references

You'll want to consult the httpd and httpd.conf man pages. Httpd and Relayd Mastery also contains many helpful examples.

Configuring

NOTE: You must replace example.com with your own domain

Copy the example file in /etc/examples/httpd.conf :

$ doas cp /etc/examples/httpd.conf /etc/httpd.conf

Edit /etc/httpd.conf :

server "example.com" {
       listen on * port 80
       location "/.well-known/acme-challenge/*" {
               root "/acme"
               request strip 2
       }
       location * {
                 block return 302 "https://$HTTP_HOST$REQUEST_URI"
       }
 }

Replace example.com to your actual hostname. On other web servers, this might be known as the virtual host.

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

The first location block in lines 3-6 responds to verification requests according to the ACME protocol. For any request that begins with http://example.com/.well-known/acme-challenge/, httpd will look for the documents in the new root /acme. Since openhttpd chroots to /var/www by default, the document root is actually /var/www/acme/. The directive request strip 2 tells openhttpd to search in /var/www/acme/ and not /var/www/acme/.well-known/acme-challenge/.

The second location block in lines 7-9 tell the web server to respond with HTTP 302 for all other requests. An HTTP 302 response forwards the web browser to a new URL address. Any user that connects to your web server using port 80, except for ACME verification, will be forwarded to use TLS on port 443 instead.

This second location block is suggested by the OpenBSD team, but for accessibility reasons, we recommend removing the second location block.

To allow plaintext requests on port 80, your conf file should now look like this:

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

Note: You must have a server block listening on port 80. Do not delete this block or else acme-client will not work. The web server needs the listener block on port 80 for ACME protocol verification.

The block for TLS on port 443 should be commented out until after you have requested TLS certs.

#server "example.com" {
#        listen on * tls port 443
#        tls {
#                certificate "/etc/ssl/example.com.crt"
#                key "/etc/ssl/private/example.com.key"
#        }
#        location "/pub/*" {
#                directory auto index
#        }
#        location "/.well-known/acme-challenge/*" {
#                root "/acme"
#                request strip 2
#        }
#}

Make sure to replace every instance of example.com with your real hostname, then check that your configuration is valid:

$ doas httpd -n

Starting the server

$ doas rcctl enable httpd
$ doas rcctl start httpd

Testing

Let's test to see if the web server is working on port 80. This test should be run on some other computer besides your web server (your home PC or phone is fine). Let's use telnet:

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

You should a response similar to the one below:

HTTP/1.0 302 Found
Date: Tue, 23 Feb 2021 14:01:28 GMT
OpenBSD httpd
Connection: close
Content-Type: text/html
Content-Length: 486
Location: https://example.com/index.html

<!DOCTYPE html>
<html> 
<head>
<meta charset="utf-8"> 
<title>302 Found</title>
<style type="text/css"><!--
body { background-color: white; color: black; font-family: 'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }
hr { border: 0; border-bottom: 1px dashed; }
@media (prefers-color-scheme: dark) {
body { background-color: #1E1F21; color: #EEEFF1; }
a { color: #BAD7FF; }
}
--></style>
</head>
<body>
<h1>302 Found</h1>
<hr>
<address>OpenBSD httpd</address>
</body>
</html>
Connection closed by foreign host.

Troubleshooting

If you were unable to establish the connection above, it may be because your firewall is blocking port 80.

You can ensure pf allows incoming http connections by putting this line into /etc/pf.conf:

pass in quick proto tcp to port {http https}

Then, reload the pf rulesets:

$ doas pfctl -f /etc/pf.conf

Adding TLS

Next, you'll want to request an SSL cert using acme-client. Then, you'll want to add TLS to openhttpd.