I love OpenBSD httpd.

Where do I start?

For the purpose of demonstration, we'll begin with a fresh installation of OpenBSD and continue from there. OpenBSD comes with httpd by default and provides an example configuration file that we'll be using. We'll also be using acme-client and its example configuration, which OpenBSD also includes by default.

Starting from scratch

Installing OpenBSD is well out of the scope of this article, so we'll be starting from first boot. First we'll copy the example files to their proper location in /etc:

cp /etc/examples/{httpd.conf,acme-client.conf} /etc

Now let's take a look at /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"
	}
}

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

Filling in your information

I'll be using the hostname www.sebastianrasor.example for the duration of the article, you should replace this with the hostname that you're planning on using. Let's go ahead and make the necessary changes to /etc/httpd.conf:

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

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

So let's go over the changes we just made; the first thing we changed is the server name, from example.com to www.sebastianrasor.example. If you're like me and will www as a subdomain, you may want to only enter the domain name instead of the entire hostname. Generally, it's best to define servers by their full hostname.

We made the next change just under the listen on * tls port 443, where it says root "/htdocs/www.sebastianrasor.example". This configuration defines where your documents are in the context of the chroot. Then we should add some content to the directory with mkdir /var/www/htdocs/www.sebastianrasor.example && echo "Hello, world!" > /var/www/htdocs/www.sebastianrasor.example/index.html.

Making it more secure

Now we're going to add the actual security configuration:

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

server "www.sebastianrasor.example" {
	listen on * tls port 443
	root "/htdocs/www.sebastianrasor.example"
	tls {
		certificate "/etc/ssl/www.sebastianrasor.example.fullchain.pem"
		key "/etc/ssl/private/www.sebastianrasor.example.key"
		ciphers "AEAD-CHACHA20-POLY1305-SHA256:AEAD-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:@STRENGTH"
		ecdhe "P-384"
		ticket lifetime default
		protocols "TLSv1.2,TLSv1.3"
	}
	hsts {
		max-age 31536000
		preload
		subdomains
	}
	location "/pub/*" {
		directory auto index
	}
	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
}

The first change we've made is to replace the certificate and key. Remember these locations for when we generate our certificate and key.

Then we define a variety of ciphers for httpd to use. The SSL Labs SSL Server Test takes both the highest and lowest strength cipher into account when scoring your website, so none of these ciphers use anything less than 256-bit encryption.

Next, we make sure to use secure curves and only to use TLS 1.2 and 1.3.

Finally, we enable HSTS, which tells browsers that the site should only be accessed using HTTPS. You should remove the subdomains option if any subdomains on your site don't use HTTPS for whatever reason.

Now we should enable and start httpd with rcctl enable httpd && rcctl start httpd.

Generating our certificates

None of this means anything without a certificate, so let's get acme-client configured by editing /etc/acme-client.conf. At the very bottom you'll see this:

domain example.com {
	alternative names { secure.example.com }
	domain key "/etc/ssl/private/example.com.key"
	domain full chain certificate "/etc/ssl/example.com.fullchain.pem"
	sign with letsencrypt
}

Let's fill in our information:

domain www.sebastianrasor.example {
	alternative names { sebastianrasor.example }
	domain key "/etc/ssl/private/www.sebastianrasor.example.key"
	domain full chain certificate "/etc/ssl/www.sebastianrasor.example.fullchain.pem"
	sign with letsencrypt
}

All that's left is generating a certificate. We can do that with acme-client -v www.sebastianrasor.example && rcctl reload httpd

And with that, we should be ready to go! You can put your new site through the SSL Server Test here.