This article focuses on httpd, the Web server. If you’re looking for a more comprehensive article that also covers relayd, the reverse proxy, you’ll find it on my other blog, OpenBSD Desktop .
OpenBSD httpd
Just to be clear, we are talking here about OpenBSD httpd, not to be confused with Apache httpd, which is generally referred to as Apache. The latter can be installed via pkg, the package manager.
OpenBSD httpd is a Web server developed by the team behind OpenBSD. For an operating system used primarily on servers, including a Web server is both logical and practical. If required, it can be paired with relayd, the reverse proxy / load balancer included with OpenBSD, which I will cover in a separate article.
Note: doas is the OpenBSD equivalent of sudo, so when a command is
preceded by doas, it is executed as an administrator.
PHP installation
Since we want to run a website written in PHP, let’s start by installing it. Simply run:
doas pkg_add php
and to select the PHP version if several are available. It is also possible to install extensions, for example:
doas pkg_add php-pdo_mysql
and to enable them:
doas ln -s /etc/php-8.5.sample/pdo_mysql.ini /etc/php-8.5/pdo_mysql.ini
To check that PHP is installed correctly, type php -v.
The Web server uses PHP FPM, which is installed alongside PHP; all you need to do is enable and start the service:
doas rcctl enable php85_fpm && doas rcctl start php85_fpm
That’s it for PHP; let’s move on to httpd.
Starting the httpd server
Before you begin, you should be aware that httpd and PHP FPM are chrooted into
/var/www/, which means they cannot see the rest of the system. When they
attempt to access the root directory, /, they actually see the contents of
/var/www/. This is a security measure: if an attacker exploits a vulnerability
in your application (or in httpd) and manages to execute commands, they will not
have access to all the files on the server; they will be restricted to /var/www/. You
do not need to do anything to enable this; it is already in place by default.
Since we’re going to be using it, let’s start the httpd service:
doas rcctl enable httpd
Let’s move on to the configuration, which is located at /etc/httpd.conf.
First, we’ll configure it for HTTP, so that we can generate a TLS certificate later.
types { include "/usr/share/misc/mime.types" }
# Here we are configuring for HTTP only
# We are configuring phpmyadmin.abosec.fr; the DNS configuration must have been
# set up beforehand
server "phpmyadmin.abosec.fr" {
# Listen on the Internet, port 80
listen on egress port 80
# The root is /var/www/htdocs/phpmyadmin/
# Keep in mind that httpd is chrooted in /var/www/
root "/htdocs/phpmyadmin"
# To generate Let's Encrypt (LE) certificates, acme-client generates
# files, stored in /var/www/acme/, which are then accessed by
# the LE infrastructure. The block below simply states that any URL
# resembling http://phpmyadmin.abosec.fr/.well-known/acme-challenge/<file>
# looks for <file> in the /var/www/acme directory, referred to as /acme
# because httpd is chrooted into /var/www/.
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
We (re)start httpd with
doas rcctl -d restart httpd
We are ready to generate the TLS certificate.
Generating the TLS certificate
Note: It is important to check that the /var/www/acme/ directory is readable
by httpd. Simply create a file called /var/www/acme/test.txt
containing ’test’ and access the URL
‘
http://phpmyadmin.abosec.fr/.well-known/acme-challenge/test.txt'
.
If you cannot see “test”, adjust the folder permissions.
Next, configure acme-client in the /etc/acme-client.conf file.
acme-client allows you to generate and regenerate certificates using the
ACME protocol. On Linux, certbot is often used for this purpose.
# We define a certificate authority, in this case Let's Encrypt
authority letsencrypt {
api url "https://acme-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-privkey.pem"
}
# Here we define the Let's Encrypt test server
# This may be useful for checking that our installation is working before
# moving to production
authority letsencrypt-staging {
api url "https://acme-staging-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-staging-privkey.pem"
}
domain phpmyadmin.abosec.fr {
# When you want to include several domains in a certificate,
# for example the www subdomain, you use 'alternative names'
#alternative names { www.phpmyadmin.abosec.fr }
domain key "/etc/ssl/private/phpmyadmin.abosec.fr.key"
domain full chain certificate "/etc/ssl/phpmyadmin.abosec.fr.fullchain.pem"
# We choose the certification authority to use
sign with letsencrypt
}
Once configured, simply run
doas acme-client phpmyadmin.abosec.fr
We can then run it as a cron job to renew the certificate automatically.
If everything went well, we can amend the httpd configuration to run over HTTPS.
httpd configuration
Back in /etc/httpd.conf, we adjust the configuration so that the site
redirects from HTTP to HTTPS.
# Here we configure it for HTTPS only
# This is the main site
server "phpmyadmin.abosec.fr" {
listen on egress port 443
# The keys are loaded when the server boots, before the chroot is applied
tls {
certificate "/etc/ssl/phpmyadmin.abosec.fr.fullchain.pem"
key "/etc/ssl/private/phpmyadmin.abosec.fr.key"
}
root "/htdocs/phpmyadmin"
location "*.php" {
# Optional: basic authentication to prevent bots from scanning phpMyAdmin
#authenticate with "/auth-files/phpmyadmin"
# Everything ending with .php is handled by PHP
fastcgi socket "/run/php-fpm.sock"
}
# ACME is now handled via HTTPS
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
# He we configure it for HTTP only
server "phpmyadmin.abosec.fr" {
listen on egress port 80
root "/htdocs/phpmyadmin"
# We redirect to the requested page, on HTTPS
block return 301 "https://phpmyadmin.abosec.fr$REQUEST_URI"
}
We restart again httpd with
doas rcctl -d restart httpd
And there we go, the Web app is now available.
Ready to put your server into production? Find out how I can migrate and host your OpenBSD infrastructure , or let’s discuss it directly .