httpd.rocks


Setup an HTTPS-enabled web server with httpd on OpenBSD.

Includes optional configuration for use with relayd or haproxy.

If you’d prefer to use something like Caddy instead, check out caddy.ninja.


Before You Begin…

This guide assumes you have already setup OpenBSD on your desired server of choice. If you need help setting up OpenBSD on a VPS, check out this helpful guide: Installing OpenBSD on Linveo KVM VPS

Most commands will need to run via doas, since you should be logged in as a created user - never root directly.

All the examples in this guide use httpd.rocks for the domains (how meta…). Please remember to change this to your desired URL.

Prep Your Domain(s)

Make sure your DNS records are setup and working as intended with your desired domain. You can check their status with:

dig httpd.rocks 

Configure Firewall (pf.conf)

Before doing anything else, you need to make sure your /etc/pf.conf is allowing traffic on ports 80 and 443. You should also include sane defaults for limiting connections (in this example, we are basing these limits off a VPS with 4GB RAM / 4 cores CPU).

Make sure you include the following:

pass in on any proto tcp from any to any port {80 443} keep state \ (max-src-conn 100, max-src-conn-rate 30/5, overload <block_table> flush global) pass out on any proto tcp from any to any port {80 443} keep state table <block_table> persist block in quick from <block_table> # Connection limits set limit states 100000 # Maximum state table entries set timeout interval 10 # Interval between state purges set optimization "normal" # General optimization for connection tracking 

Once that is saved, simply reload the ruleset:

pfctl -f /etc/pf.conf 

Setting Up httpd

Make initial website folder and files:

doas mkdir -p /var/www/htdocs/httpd.rocks 

Place your website files into this new folder and set proper permissions:

doas chmod -R 755 /var/www/htdocs/httpd.rocks doas chown -R www:www /var/www/htdocs/httpd.rocks 

Create the initial /etc/httpd.conf file:

server "httpd.rocks" { listen on * port 80 root "/htdocs/httpd.rocks" } 

Check that the configuration has no errors, then get httpd up and running:

doas httpd -n doas rcctl start httpd 

Note: If you do encounter runtime errors with httpd, you might be required to add the following to your /etc/rc.conf.local file:

httpd_flags="" 

If everything was setup properly, you should be able to visit the HTTP-only version of your website online. The only problem is HTTPS isn’t setup…

..yet!

Configure acme-client.conf

Before anything else, we need to create proper directories for acme-client (our next steps) and set their permissions:

doas mkdir -p -m 750 /etc/ssl/private doas mkdir -p -m 755 /var/www/acme 

Create the /etc/acme-client.conf file and include the following:

authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" } domain httpd.rocks { alternative names { www.httpd.rocks } domain key "/etc/ssl/private/httpd.rocks.key" domain full chain certificate "/etc/ssl/httpd.rocks.crt" sign with letsencrypt } 

Now we can run the core acme-client command to generate our certificates:

doas acme-client -v httpd.rocks 

If everything goes smoothly, your new certificates should be generated and issued. The next thing you will want to do is automatically check for expired certs.

Then create a separate script (this will be helpful if you plan to host multiple sites on a single server). Name it something like renew_certs.sh and save it under a local directory (ie. /home/username/scripts):

#!/bin/sh DOMAINS="httpd.rocks example1.com example2.com example3.com" echo "Checking certificates for each domain..." # Loop through each domain and run acme-client only if renewal is needed for DOMAIN in $DOMAINS; do if ! doas acme-client -n "$DOMAIN"; then echo "Certificate for $DOMAIN needs renewal, running acme-client..." doas acme-client "$DOMAIN" || exit 1 else echo "Certificate for $DOMAIN is still valid, skipping renewal." fi done # Reload httpd after updating certificates echo "Reloading httpd..." doas rcctl reload httpd echo "httpd reloaded successfully." 

For reference I have included multiple domains if you decide to host several websites through one server. Remove these if you only plan to host a single domain.

Set executable permissions on this script:

doas chmod +x /path/to/renew_certs.sh 

Then setup the following cronjob by running crontab -e and entering in:

0 0 * * * doas -u <user> /path/to/renew_certs.sh 

Replace <user> with your username. Certificate automation has been completed!

Configuring HTTPS

Next we will serve our site(s) through HTTPS. For most static sites or personal blogs, using the built-in TLS support from httpd should be perfectly fine. If you have a need for custom security headers or a transparent proxy take look at the additional guides below.

Using relayd or haproxy (optional)

But for our needs we will continue with using base httpd.

Tweaking httpd.conf

Return to your previous httpd.conf and include the following changes:

server "httpd.rocks" { alias "www.httpd.rocks" listen on * tls port 443 root "/htdocs/httpd.rocks" hsts location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } tls { certificate "/etc/ssl/httpd.rocks.crt" key "/etc/ssl/private/httpd.rocks.key" } } server "httpd.rocks" { alias "www.httpd.rocks" listen on * port 80 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } block return 301 "https://$SERVER_NAME$REQUEST_URI" } 

The changes are fairly straightforward. The alias saves us the trouble of creating a separate server block just for www targets. We also now include our acme-generated certificates inside the server block that handles traffic through port 443 and simply redirect all standard HTTP traffic (port 80) to that block. The included hsts tells the browser to use HTTPS by default, so we can avoid extra unnecessary redirects.

The .well-known directory location is required for when we need to renew our SSL certificates (based off the renew script above).

Now restart the service:

doas rcctl restart httpd 

Redirecting www Traffic

Now we are in the final stretch. Everything should be up-and-running, except you are now serving both www and non-www as independent entities. This is fine, but you should normally stick to one variation. You can edit your httpd.conf with the changes below in order to setup automatic redirects of www traffic to non-www:

server "httpd.rocks" { listen on * tls port 443 root "/htdocs/httpd.rocks" hsts location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } tls { certificate "/etc/ssl/httpd.rocks.crt" key "/etc/ssl/private/httpd.rocks.key" } } server "httpd.rocks" { listen on * port 80 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } block return 301 "https://$SERVER_NAME$REQUEST_URI" } server "www.httpd.rocks" { listen on * port 80 listen on * tls port 443 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } tls { certificate "/etc/ssl/httpd.rocks.crt" key "/etc/ssl/private/httpd.rocks.key" } block return 301 "https://$SERVER_NAME$REQUEST_URI" } 

We remove the original alias parameter and create an additional server block (www focused). Pretty simple stuff.

Once again, restart the service:

doas rcctl restart httpd 

It’s Alive!

Now check out your website! Everything should work perfectly!

You should have valid TLS, your standard HTTP request should forward to HTTPS, and all www requests should forward to non-www.

That’s it!


Extras

HTTP Basic Authentication

OpenBSD ships with the built-in htpasswd for configuring user authenicated sites with httpd.

Important: it is best practice to always serve basic authentication logins over HTTPS-only.

First, create the main httpd.passwd file and associated user:

htpasswd /var/www/etc/httpd.passwd youruser 

Also make sure to give this file proper permissions:

doas chown www /var/www/etc/httpd.passwd doas chmod 600 /var/www/etc/httpd.passwd 

Then enter a strong password once prompted. Next we will need to tweak our httpd.conf files. In the example below, we plan to password protect the secure.html file on httpd.rocks:

server "httpd.rocks" { listen on * tls port 443 root "/htdocs/httpd.rocks" hsts location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location "/secure.html" { authenticate "Restricted Area" with "/etc/httpd.passwd" } tls { certificate "/etc/ssl/httpd.rocks.crt" key "/etc/ssl/private/httpd.rocks.key" } } 

Then reload httpd and you’re done:

rcctl restart httpd 

Now navigating to your secure.html page on your website will prompt for a username and password. You can test it yourself here: httpd.rocks/secure.html (login: dog password: cat)

References

Please refer to these additional (and mostly better) resources, books, and documentation:


Contribute

I’m far from an OpenBSD expert! Please help improve this project!