Want to setup a WP2Static-friendly, minimal webserver vs using an off the shelf image? We detail the steps for setting up an optimized environment in OpenBSD, our preferred, secure by default, lightweight and easy to maintain operating system. You can also use this as a guide for configuring webservers on other platforms.

This guide is written for and maintained for the latest -current version of OpenBSD. If you’re using a stable release, some changes may be required.

We offer support for users on nginx, Apache, IIS and other webservers, but our primary development and testing machines are OpenBSD, so we can best support you when using it.

Before asking for support, invest some time in searching the comprehensive man pages, ie man httpd or man httpd.conf.

Security note: This guide is for setting a non-public server for your WordPress development site, not for production hosting your exported static site. WP2Static’s benefits allow for a less-secure WordPress installation. We’re not in the business of hosting production sites. See Deployment options for many production hosting options for your exported static site.


We have most of what we need in base: the httpd(8) webserver, acme-client(1) (for optional TLS certificates).

From the ports tree, we can install the packages below. Format here is for copying into a list file and using like doas pkg_add -Dsnap -l list, else install just those you want, by removing the trailing --, ie doas pkg_add -Dsnap php, which may ask you to choose the target version.

For WordPress


For WP2Static


Optional utilities that may help in development/troubleshooting

vim--no_x11 (`vi(1)` and `ed(1)` editors are already installed in base)
node-- (optional, for WP2Static development, compiling frontend from source code)
py3-pip-- (for installing AWS CLI client)

Webroot permissions

In this guide, we’re setting up just one WordPress instance in the webserver document root (/var/www/htdocs). To allow the www user to write to this directory, we’re setting the user and group access to the dir:

doas chown -R www:www /var/www/htdocs

Because this is a non-public, development server, we’re not hardening the WordPress installation/webserver as one should for production use.

Ensure you create files similarly owned by www:www.

Testing the httpd(8) webserver

On a fresh install of OpenBSD, there is no web service running.

Let’s put a quick test configuration in place and sanity-check that we can serve a webpage:

# /etc/httpd.conf
# a minimal config to serve static content from /var/www/htdocs/

types { include "/usr/share/misc/mime.types" }

server "default" {
	listen on * port 80

	root "/htdocs"

	location "/*" {
		directory auto index
<!-- /var/www/htdocs/index.html -->
<!-- file owner should be www: chown www:www /var/www/htdocs/index.html --> 
Test webserver is running!
# set the webserver to run on system start/allow manually starting
doas rcctl enable httpd

# (re)start the webserver
doas rcctl restart httpd

Test the webserver is serving our file by accessing http://servername from a browser, or on the server itself, run:

ftp -o - http://localhost and you should see:

Requesting http://localhost
  0% |                                                      |     0       --:-- ETA
<!-- /var/www/htdocs/index.html -->
<!-- file owner should be www: chown www:www /var/www/htdocs/index.html -->
Test webserver is running!
100% |******************************************************|   140       00:00
140 bytes received in 0.00 seconds (74.49 KB/s)

Great! Now, let’s add support for PHP and MySQL, so we can install WordPress:

# /etc/httpd.conf
# a minimal config to serve static content from /var/www/htdocs/

types { include "/usr/share/misc/mime.types" }

server "default" {
	listen on * port 80

  directory index "index.php"

	root "/htdocs"

	location "/*.css*" {
		request no rewrite

	location "/*.js*" {
		request no rewrite

	location "/*.png*" {
		request no rewrite

	location "/wp-admin/*.php" {
		fastcgi socket "/run/php-fpm.sock"

	location "/wp-admin*" {
		fastcgi socket "/run/php-fpm.sock"
		request rewrite "/wp-admin/index.php"

	location "*.php" {
		fastcgi socket "/run/php-fpm.sock"

	location "/*" {
		fastcgi socket "/run/php-fpm.sock"
		request rewrite "/index.php"

<?php // /var/www/htdocs/index.php
// file owner should be www: chown www:www /var/www/htdocs/index.php
echo 'PHP is now running on the server!';
# restart the webserver to apply new configuration
doas rcctl restart httpd

# Enable and (re)start the PHP service
doas rcctl enable php73_fpm
doas rcctl restart php73_fpm

Test the webserver is now serving PHP by accessing http://servername from a browser, or on the server itself, run:

ftp -o - http://localhost and you should see:

Requesting http://localhost
PHP is now running on the server!33 bytes received in 0.00 seconds (151.31 KB/s)

Installing WordPress via WP-CLI

As per our guide on WP-CLI usage for WP2Static, we install WP-CLI as follows:

# download WP-CLI phar to temp directory
cd /tmp
ftp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

# test execution
php wp-cli.phar --info

# should result in:
OS:     OpenBSD 6.6 GENERIC.MP#584 amd64
Shell:  /bin/ksh
PHP binary:     /usr/local/bin/php-7.3
PHP version:    7.3.13
php.ini used:   /etc/php-7.3.ini
WP-CLI root dir:        phar://wp-cli.phar/vendor/wp-cli/wp-cli
WP-CLI vendor dir:      phar://wp-cli.phar/vendor
WP_CLI phar path:       /tmp
WP-CLI packages dir:
WP-CLI global config:
WP-CLI project config:
WP-CLI version: 2.4.0

# move to path with name `wp` and execution permissions
doas mv wp-cli.phar /usr/local/bin/wp
doas chmod +x /usr/local/bin/wp

# test execution again (expect same results)
wp --info

Add local user to www group, allowing to create/edit files within webroot.

doas usermod -G www MYUSERNAME
# log out and in to use new permissions

Download WordPress files

cd /var/www/htdocs
wp core download

Should show output like:

Downloading WordPress 5.3.2 (en_US)...
Warning: Failed to create directory '/var/www/.wp-cli/cache/': mkdir(): Permission denied.
md5 hash verified: ec7fcc299de72d4914a688ea34c96f37
Success: WordPress downloaded.

You should also be able to list the WordPress core files in /var/www/htdocs/ now and accessing http://localhost should contain a message like:

There doesn’t seem to be a wp-config.php file. I need this before we can get started.

MySQL database setup

This can be done earlier in the process, but for the sake of this guide, we wanted to ensure all the filesystem stuff is working first.

For the first draft of this guide and again, as we’re setting up a non-public devserver, we’ll use the quickest, but less secure method of getting a DB prepared for WordPress.

# prepare mysql
doas mysql_install_db
# follow prompts, using root user and for example, password 'banana'

# enable and start service
doas rcctl enable mysqld
doas rcctl start mysqld

# config DB
doas mysql_secure_installation
# follow prompts, using root user and for example, password 'banana'

# create new DB for WordPress
mysql -u root -p

> create database wordpress;

Generate a wp-config.php file

cd /var/www/htdocs/
wp config create --dbhost= --dbname=wordpress --dbuser=root --dbpass=banana

Install WordPress

cd /var/www/htdocs/
wp core install --url=localhost --title='WP Dev Server' --admin_user=admin --admin_password=banana --admin_email=user@example.com

Resulting in:

Success: WordPress installed successfully.

Visiting the site now, you should see a regular WordPress site with default dummy content. You can login to administer via /wp-admin and the credentials you specified earlier.

Installing WP2Static

At this point, you have a few options for installing WP2Static and its add-ons:

  • clone git repository (core and/or add-ons) and compile from latest source code
  • download plugin core and any add-ons you’ve got a license for via local or remote zip file
  • download latest official version from wordpress.org via WP-CLI
  • install latest version via the WordPress admin UI

Installing free version from wp.org via WP-CLI

cd /var/www/htdocs/
# official plugin slug is not "wp2static" due to legacy naming
# we'd love to change this, but wp plugin team don't budge on renaming :(
wp plugin install --activate static-html-output-plugin

Installing from cloned sources for development

# remove plugin version installed via WP admin UI
wp plugin deactivate static-html-output-plugin
wp plugin delete static-html-output-plugin

# clone core repo to projects dir
mkdir -p ~/gitprojects && cd $_
# use legacy plugin name for project
git clone git@github.com:WP2Static/wp2static.git static-html-output-plugin
cd ~/gitprojects/wp2static
# install dependencies
composer install
npm i
# compile frontend
composer buildjs

Use this script to do an initial sync and continue to sync while watching project dir:


# usage: run `sh this_script.sh` in its own shell

# set your project and WP plugin dirs


# initial sync of files
openrsync --rsync-path /usr/bin/openrsync --del -var "$WP2STATIC_CORE_DIR" "$WP_PLUGIN_DIR"/

while true; do
  # doesn't wach ignore files, but will sync whole dir
  ag -al --path-to-ignore "$WP2STATIC_CORE_DIR"/.gitignore | entr -d openrsync --rsync-path /usr/bin/openrsync --del -var "$WP2STATIC_CORE_DIR" "$WP_PLUGIN_DIR"/

Exporting your site with WP2Static

Following this guide up to here, we’ve not needed to login to the WordPress admin or use a browser. We can continue to work on the command line to export our site using WP2Static.

Here, we’ll do the minimal steps required to export the WordPress site to a directory, replacing all Site URLs to our target production URL.

cd /var/www/htdocs/
# set the exported site's URL to what you'll use in production
wp wp2static options set baseUrl https://example.com
# generate the static site
wp wp2static generate
# get generated directory path
"$(cat wp-content/uploads/WP2STATIC-CURRENT-ARCHIVE.txt)"

Deploy your generated static site

Use one of the many deployment options to publish your site (ideally to a staging server to check first!)

We recommend checking the exported site with Netlify in our Doing a test export guide, at least when first getting familiar with WP2Static and static site deployments. For this case, you can either run Netlify’s CLI tool, pointing it to the exported site directory, or use the ZIP deployment method and manually upload that using their web interface.