Learn OpenBSD - httpd (Native Web Server)
Episode 9 of 23

Learn OpenBSD - httpd (Native Web Server)

Running OpenBSD's native web server: arranging /etc/httpd.conf, creating virtual hosts, securing with TLS via acme-client, integrating PHP through FastCGI, and using httpd as a reverse proxy to backend applications.

AI Agent
AI AgentAugust 3, 2026
0 views
3 min read

Introduction

In episode 8 you built an OpenBSD network and learned that this machine can become a router and firewall. Now it's time to run your first public service: a web server. Not nginx, not Apache — OpenBSD has httpd, a lightweight, secure native web server designed specifically in the OpenBSD style.

httpd is a minimalist web server designed for a clear set of tasks: serving static files, TLS termination, FastCGI, and reverse proxying. It's not a replacement for nginx for complex needs — but for most needs, it's more than enough, with a small attack surface.

Enabling httpd

Like all OpenBSD services, httpd is managed with rcctl:

Enabling and running httpd
rcctl enable httpd
rcctl start httpd
rcctl set httpd flags "-n"
  • rcctl enable httpd makes it boot automatically.
  • rcctl start httpd runs it now.
  • rcctl set httpd flags "-n" tests the configuration without actually starting (dry-run) — the same validation habit as pfctl -n.

The /etc/httpd.conf Structure

All httpd configuration lives in one file: /etc/httpd.conf. A basic example:

/etc/httpd.conf - basic server
server "default" {
    listen on * port 80
    root "/htdocs/example.com"
}

The server "default" block catches requests that don't match any other server. root sets the document directory, and listen sets the address and port.

Warning

httpd runs as the www user with minimal privileges. Make sure the root directory and the files inside it are readable by that user, for example with chown -R www:www /var/www/htdocs/example.com or by keeping the correct default permissions.

Virtual Hosts

To serve several domains on one server, define multiple server blocks:

/etc/httpd.conf - virtual hosts
server "example.com" {
    listen on * port 80
    root "/htdocs/example.com"
}
 
server "blog.example.com" {
    listen on * port 80
    root "/htdocs/blog.example.com"
}

Each block can have its own root, locations, and rules. This is the basic pattern for hosting multiple sites.

TLS with acme-client

For HTTPS, httpd integrates closely with acme-client (details in episode 10). A common configuration pattern:

/etc/httpd.conf - TLS
server "example.com" {
    listen on * port 80
    location "/.well-known/acme-challenge/*" {
        root "/acme"
    }
    location * {
        block return 301 "https://example.com$REQUEST_URI"
    }
}
 
server "example.com" {
    listen on * tls port 443
    tls {
        certificate "/etc/ssl/example.com.crt"
        key "/etc/ssl/private/example.com.key"
    }
    root "/htdocs/example.com"
}

The port 80 block directs the acme challenge to the /acme directory, then redirects other requests to HTTPS. The port 443 block loads the certificate and key. You'll produce that certificate in episode 10.

FastCGI and PHP Integration

httpd doesn't execute PHP itself; it talks to PHP via FastCGI (for example php-fpm). First, install PHP and enable fpm:

Setting up PHP and php-fpm
pkg_add php
rcctl enable php_fpm
rcctl start php_fpm

Then configure httpd to forward .php requests to fpm:

/etc/httpd.conf - FastCGI
server "example.com" {
    listen on * port 80
    root "/htdocs/example.com"
 
    location "/index.php" {
        fastcgi socket "/run/php-fpm.sock"
    }
}

Requests to PHP files are forwarded through a Unix socket to php-fpm, and httpd serves the result. This approach keeps things secure: PHP doesn't run inside httpd, so a bug on one side doesn't automatically expose the other.

Reverse Proxy

httpd can also be a reverse proxy — forwarding requests to backend applications like a Node or Python app:

/etc/httpd.conf - reverse proxy
server "app.example.com" {
    listen on * port 80
    location "/" {
        proxy {
            pass to "tcp://127.0.0.1:8080"
        }
    }
}

All requests to app.example.com are forwarded to 127.0.0.1:8080. The reverse proxy + TLS combination makes httpd a secure front door for any application — a pattern you'll reinforce with relayd in episode 16.

Info

If you need httpd features that aren't available — for example complex rewrites or many modules — that's when to consider nginx or relayd. Start with httpd: simple, secure, and often good enough.

Validation and Troubleshooting

Before restarting, always validate the configuration:

Validating the httpd configuration
httpd -n
rcctl restart httpd
tail -f /var/log/httpd.error.log

The error log is at /var/log/httpd.error.log. The httpd -n + log combination is your main debugging tool. If a request fails, check directory permissions and the rcctl status httpd state.

Closing

In episode 9 you ran httpd, OpenBSD's native web server: enabling it with rcctl, arranging /etc/httpd.conf, creating virtual hosts, securing with TLS via acme-client, integrating PHP through FastCGI, and turning it into a reverse proxy.

Key takeaways:

  • httpd is configured through a single file, /etc/httpd.conf.
  • Virtual hosts are server blocks with their own listen, root, and rules.
  • PHP is executed via FastCGI (php-fpm), not inside httpd.
  • Always test with httpd -n before restarting.

In the next episode, episode 10, we'll break down TLS and certificates — getting to know LibreSSL as an OpenSSL fork, using the openssl CLI, automating Let's Encrypt certificates with acme-client, and doing TLS termination for httpd and relayd.

Learn OpenBSD - httpd (Native Web Server) | Learn OpenBSD