WordPress, Lighttpd, and Cloudflare

I’ve been meaning to try Lighttpd for ages as I wanted to downgrade my server to save money and it takes the least amount of memory compared with apache or nginx.

Installed with the help of:
https://www.how2shout.com/linux/install-wordpress-on-lighttpd-web-server-ubuntu/
https://www.howtoforge.com/how-to-install-lighttpd-with-php-and-mariadb-on-debian-10/
But had to make changes too.

james@instance-1:~$ sudo mysql
CREATE DATABASE mydatabase CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
create user 'demoh2s'@'localhost' identified by 'password';
GRANT ALL PRIVILEGES ON `mydatabase`.* to `demoh2s`@localhost;
exit
james@instance-1:~$ sudo apt install php php-cgi php-cli php-fpm php-curl php-gd php-mysql php-mbstring zip unzip php-zip php-xml php-intl php-imagick apache2-

I’m installing on Ubuntu 22.04 Minimal https://wiki.ubuntu.com/Minimal on Google’s cloud platform. I try to keep original config files intact so have only altered the server.document-root and rewrite rules in lighttpd.conf

There is a tool similar to the one in apache to install the symlinks in /etc/lighttpd/conf-enabled

james@instance-1:~$ sudo lighty-enable-mod fastcgi fastcgi-php auth deflate rewrite accesslog

Rewriting

At first all the permalinks were showing ‘index.php’ which apache wasn’t doing before so I gathered together some rewriting rules from various places via Google. Through trial and error I ascertained that the order is significant, and I’ve included the rules I found but commented out. These still aren’t definitive but seem to be a work in progress and WordPress only seems to need the 4 uncommented ones at present. You can Google them to see where they came from:

lighttpd "^/(wp-admin.+).*/?" => "$0"

The Lighttpd wiki is very useful too: https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs

james@instance-1:/etc/lighttpd$ cat lighttpd.conf
server.modules = (
	"mod_indexfile",
	"mod_access",
	"mod_alias",
 	"mod_redirect",
)

#url.rewrite = (
#"^/?$" => "/index.php",
# Exclude common directories
#"^/(wp-.+)$" => "$0",
# Exclude letsencrypt certbot
#"^/(.well-.+)$" => "$0",
#"^/xmlrpc.php" => "$0",
#"^/sitemap.xml" => "$0",
# Handle permalinks and feeds
#"^/(.+)/?$" => "/index.php/$1"
#)

url.rewrite-if-not-file = (
 "^/(wp-admin.+).*/?" => "$0",
# Exclude letsencrypt certbot
 "^/(.well-.+)$" => "$0",
# REST API for block editor & Jetpack 
 "(?:\?(.*))?$" => "/index.php?$1",
# "(\/styles\/|\/images\/|\/scripts\/)" => "$0",
 "^/(wp-.+).*/?" => "$0",
# "^/images/.*/?" => "$0",
# "^/temp/.*/?" => "$0",
# "^/keyword/([A-Za-z_0-9\-]+)/?$" => "/index.php?keyword=$1",
# "^/.*?(\?.*)?$" => "/index.php$1"
)

server.document-root        = "/var/www/html/xyze.co.uk"
.
.
.

You need to set the permalinks in wp-admin as well under Settings, Permalinks:

Custom Structure https://www.xyze.co.uk/index.php /%postname%/

WordPress (this is not needed if using Let’s Encrypt)

The way I’ve installed WordPress before is to just use http and then use the ‘Flexible’ shared Cloudflare certificate that encrypts traffic from the user to Cloudflare but then its served to Cloudflare using http. This has worked fine with apache but both the classic editor and block editor weren’t working in Lighttpd and searching with the error from the block editor I stumbled across this:
https://wordpress.org/support/topic/publishing-failed-you-are-probably-offline/

james@instance-1:~$ cat /var/www/html/xyze.co.uk/wp-config.php

/* Add any custom values between this line and the "stop editing" line. */

define( 'WP_SITEURL', 'http://xyze.co.uk' );
define( 'WP_HOME', 'http://xyze.co.uk' );

/* That's all, stop editing! Happy publishing. */

This allowed me to use http if I wanted to add or edit a post and Cloudflare’s Flexible https worked too.

Let’s Encrypt

However now we have Let’s Encrypt with certbot, its better to have proper https.

james@instance-1:~$ sudo apt install certbot
james@instance-1:~$ sudo certbot certonly --webroot -w /var/www/html/xyze.co.uk/ -d www.xyze.co.uk

https://eff-certbot.readthedocs.io/en/stable/using.html
Says that “For historical reasons, the containing directories are created with permissions of 0700 meaning that certificates are accessible only to servers that run as the root user. If you will never downgrade to an older version of Certbot, then you can safely fix this using chmod 0755 /etc/letsencrypt/{live,archive}”
Which I did.

This time I copied /etc/lighttpd/conf-available/10-ssl.conf to 10-ssl-xyze.conf and then created the symlink manually.

james@instance-1:/etc/lighttpd/conf-enabled$ sudo ln -s ../conf-available/10-ssl-xyze.conf
james@instance-1:/etc/lighttpd/conf-enabled$ cat 10-ssl-xyze.conf
# /usr/share/doc/lighttpd/ssl.txt
# -*- conflicts: mbedtls, gnutls, nss, wolfssl -*-

server.modules += ( "mod_openssl" )

# ssl.* in global scope gets inherited by
#   $SERVER["socket"] == "..." { ssl.engine = "enable" }
#ssl.pemfile = "/etc/lighttpd/server.pem"
ssl.pemfile = "/etc/letsencrypt/live/www.xyze.co.uk/fullchain.pem" # Combined Certificate
ssl.privkey = "/etc/letsencrypt/live/www.xyze.co.uk/privkey.pem"

ssl.cipher-list = "HIGH"

$SERVER["socket"] == ":443" {
ssl.engine = "enable"
}

$HTTP["scheme"] == "http" {
$HTTP["host"] == "www.xyze.co.uk" { # HTTP URL
url.redirect = ("/.*" => "https://www.xyze.co.uk$0") # Redirection HTTPS URL
}
}

include_shell "/usr/share/lighttpd/use-ipv6.pl 443"

At first I copied the example from the howtoforge link above but lighttpd kept failing to restart producing an error like this: ssl.pemfile has to be set in same $SERVER[“socket”] scope as other ssl.* directives

Eventually I worked out that the pem files had to be in the global file or in all of the socket, scheme, and host directives, so I moved it out of the socket directive so they’re now global and inherited by all of them.

Finally I changed the wp-config.php to https – or you could comment them out as long as they’re set to https in Settings, General on the WordPress Dashboard. And Cloudflare can now use ‘Full’ encryption and ‘Always Use HTTPS’.

Cloudflare

I added some Firewall rules using the dash console:

WordPress Spam Filter
(http.request.uri.path contains "wp-comments-post.php") or (http.request.uri.path contains "wp-login.php")
Managed Challenge

Protect the wp-admin Area
(http.request.uri.path contains "/wp-admin/" and not http.request.uri.path contains "/wp-admin/admin-ajax.php" and not http.request.uri.path contains "/wp-admin/theme-editor.php")
Managed Challenge

Block xmlrpc.php Attacks
(http.request.uri.path contains "/xmlrpc.php")
Block

Block wp-login
(http.request.uri.path contains "wp-login.php" and ip.geoip.country ne "GB")
Block

Postscript

I followed a guide to install Redis here except I installed the php client as it seemed to work faster. This uses a WordPress plugin from here. Php will need reloading but I rebooted as I’d also installed some kernel updates.

james@instance-1:~$ sudo apt install php-redis

I also installed a Cloudflare cache plugin from here which needs your api key from Cloudflare and works really well. There is also the W3 Total Cache in place of the redis cache plugin.

Leave a comment