Back in 2019, I wrote a couple of blog posts outlining how to set up a web server called lighttpd to run some simple websites. Those two posts were called Setting up Lighttpd for Simple Sites and Adding SSL / https to lighttpd.
This post updates those, for a few reasons:
- For reasons, I found myself wanting to install lighttpd on AlmaLinux, rather than Debian. These days, even low-end VPS come with at least 512 MB of RAM, which is enough to run the yum / dnf package manager, so low-RAM was no longer pushing me to Debian.
- SSL / TLS / https is now used on every website. It's not an important option but a necessity. That needs setting up sooner, rather in a second follow-on post for the extra keen.
- Technology has moved on. In particular, the world of ciphers in a package like openssl is different today.
Much is the same, including the fact that lighttpd is much more light-weight than, say, apache2. So here is a single post to get you up and running with lighttpd on AlmaLinux / Rocky Linux / CentOS, Fedora, RHEL, etc.
I’ll keep the write-up briefer this time, because one post will cover the whole process.
One big difference between a RHEL-derived rpm install and a Debian / Ubuntu apt install is that you don't get a command-line tool lighty-mod-enable to enable modules. It has to be done manually, but it’s not hard.
Basic Installation
This time, we use yum / dnf rather than apt
sudo dnf install lighttpd
That will put a configuration file at /etc/lighttpd/lighttpd.conf, and the pages hosting your website will be stored in /var/www/lighttpd.
Before starting the service, out of the box, the lighttpd.conf file tells lighttpd to support IPv6. If you want your server to listen for connections over IPv4, you need to change that, otherwise it will be listening for connections only from :::*. To switch over to IPv4, open up lighttpd.conf in a text editor, and find the line that sets lighttpd server.use-ipv6 to enable. Change it to disable, then it will listen to 0.0.0.0:* as required.
Make sure ports 80 and 443 are open inbound through your firewall, then start the service:
sudo systemctl enable --now lighttpd
SSL won’t work yet (we haven't set that up), but regular http traffic should. If you check http://{your-full-hostname}, you should see the “Powered by lighttpd” page with the lighttpd logo. It’s likely your browser will redirect to https, so you may need to test on the command line:
curl http://{your-full-hostname}
If that works, the lighttpd web server is running, and we can proceed to the next step.
Set up TLS / SSL / https
To do this, we need to create a valid ssl certificate for your server's hostname first, and then configure lighttpd to serve pages over https. If you try and change the configuration first, lighttpd won’t start because the certificate files don't exist. We’ll use letsencrypt. Install their certbot tool for requesting certificates
sudo dnf install certbot
Then we create a bash file, very similar to the one in the 2019 post, to request a certificate for your hostname. This needs http traffic to work, so certbot can create a test file on the /.well-known path, and then connect to it remotely using your hostname to prove your webserver resolves that hostname. That’s why this works before we get SSL configured.
Note: lighttpd has moved on from 2019, and you no longer need to concatenate the certificate and private key into a single file. Here's the script to create and run:
#!/bin/bash
/usr/bin/certbot certonly \
--rsa-key-size 4096 \
--email {your-email@your-domain.com} \
--renew-by-default \
--agree-tos \
--text \
--webroot \
--webroot-path /var/www/lighttpd \
--domain $(hostname)
rm /var/www/html/.well-known -rfvYou should see a success message, and it should report where it has saved the certificate and private key. Probably the private key is /etc/letsencrypt/live/{your-host-name}/privkey.pem and the certificate is /etc/letsencrypt/live/{your-host-name}/fullchain.pem.
Assuming that worked, we can configure lighttpd. Edit lighttpd.conf and find the section headed “SSL Support”.
Enable the openssl module (remember: no lighty-mod-enable) by uncommenting the line that says server.modules += ( "mod_openssl" ).
Uncomment the lines that set ssl.privkey and ssl.pemfile, and set those to the full path to your private key and certificate respectively.
Lastly, there is still the need to make sure only secure ciphers and protocols are used. In 2019, I suggested setting the cipher list to all the ones you want to allow. The problem is that new, more secure, ciphers could be designed, and an allow-list approach would reject those. So we follow the advice of Mozilla, and set the cipher list to be everything with high security except those using certain insecure technologies. We also need to tell lighttpd not to use SSLv2 or SSLv3 ciphers at all. Again the most future-proof way is to say that the protocol must be at least TLSv1.2. Doing that, rather than specifying TLSv1.2 and TLSv1.3, ensures that TLSv1.4 works when it appears. So you need these three lines in lighttpd.conf:
ssl.cipher-list = "HIGH:!aNULL:!eNULL:!PSK:!kRSA:!MD5:!RC4:!SHA1:!EXPORT"
ssl.honor-cipher-order = "enable"
ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2") There's one final step, which is easy to miss. Without this, lighttpd will start cleanly, but no https traffic will work. You need to tell it to listen on port 443 for SSL traffic. Uncomment these three lines:
$SERVER["socket"] == "*:443" {
ssl.engine = "enable"
}(or the [::]:443 ones if you're using IPv6)
You should now be able to restart lighttpd (sudo systemctl restart lighttpd), and browse your hostname over https (this time, in a web browser): https://{your-host-name}.
Note, in 2019, I had us generating stronger Diffie-Hellman parameters. Modern versions of openssl, and the ciphers we have left available, either don't use Diffie-Hellman or use parameters with at least 2048 bits (which are secure enough). This step is not necessary.
Enable Access Logging
The first 2019 post contained a section on setting lighttpd to write access logs and error logs. Happily, the current version installed via rpm comes with that already set up. By default, it logs all traffic to /var/log/lighttpd/access.log, and errors to /var/log/lighttpd/error.log. So there’s nothing to do this time.
To change the folder used for logging, edit var.log_root in lighttpd.conf. To edit the filename of the access log, edit accesslog.filename in /etc/lighttpd/conf.d/access_log.conf. To edit the filename of the error log, edit server.errorlog in lighttpd.conf.
Virtual Hosts
This is something else that has got easier since 2019. They now include a template file for creating virtual hosts that you just have to copy and adapt, making it easy for those virtual host configurations to be included.
One thing that is the same is the order of attack. You’ll almost certainly want to use your new virtual host over https. lighttpd won’t start if the configuration mentions certificate files that don’t exist. So you need to create the virtual host without ssl, create your new certificate, then enable ssl.
At the bottom of lighttpd.conf, uncomment the line that includes everything in vhosts.d:
include conf_dir + "/vhosts.d/*.conf"
Go and find the /etc/lighttpd/vhosts.d directory. In there is a sample file called vhosts.template. Create a copy of that in the vhosts.d directory, named after the domain you want to host. Say you want to host subdomain.example.com, you’d call the file something like subdomain.example.com.conf. The important bit is the .conf on the end; everything else in the name is to help you.
Edit this configuration file. Change the $HTTP["host"] declaration so it matches the hostname of your virtual host. (You can use things like regular expressions (~=) if you wish.) For example:
$HTTP["host"] == "subdomain.example.com"
Change the var.server_name too. This can be anything, but you make your job easier later if it also matches your virtual host domain name:
var.server_name = "subdomain.example.com"
Lastly, change the server.document-root line. Unless you want it to go to a subdirectory of /var/www, you probably want to set it to something like /home/subdomain/public_html. That's up to you. You then need to create the document root directory (mkdir -p), and if necessary the account that will own it (useradd). Place an index file in that directory to test with:
echo "Hello world!" > /home/subdomain/public_html/index.html
Restart lighttpd (sudo systemctl restart lighttpd) so your new virtual host is picked up.
Here are two things you may need to take care of:
- The lighttpd server runs as user
lighttpd. This needs to be able to read any files you put in/home/subdomain/public_html. Make sure/homeand/home/subdomainboth have permissions 755 not 700, and so on. - If your server is selinux enforcing, you may need to turn on a couple of permissions. lighttpd needs to read user-created content (so check with
getsebool httpd_read_user_content, then set withsetsebool -P httpd_read_user_content on). If that content is within the system-widehomepath, you’d need to allow lighttpd to read from there (check withgetsebool httpd_enable_homedirs, then set withsetsebool -P httpd_enable_homedirs on).
Once you've checked (again, probably with curl rather than in a browser) that your virtual host is working over http, it's time to set up SSL. This is similar to above, where we did this for the server hostname. Create a bash file as follows, using your domain name, your webroot path, and your email:
#!/bin/bash
/usr/bin/certbot certonly \
--rsa-key-size 4096 \
--email {your-email@your-domain.com} \
--renew-by-default \
--agree-tos \
--text \
--webroot \
--webroot-path /home/subdomain/public_html \
--domain subdomain.example.com \
--domain www.subdomain.example.com
rm /home/subdomain/public_html/.well-known -rfvAs with the hostname certificate, you should see a success message, giving you the full path to the private key and certificate files. Go back to your vhost configuration file, and uncomment the two lines beginning ssl.pemfile and ssl.privkey. Put your certificate and private key (respectively) in there. (The default values for these files, given in the virtual host template file you copied, are based off a vhosts_dir variable. Make sure to set exactly the path you want, without including that):
ssl.pemfile = "/etc/letsencrypt/live/subdomain.example.com/fullchain.pem"
ssl.privkey = "/etc/letsencrypt/live/subdomain.example.com/privkey.pem"You should now be able to check that your domain loads in a web browser over https.
Lastly, to have access and error log files specific to your new virtual host, uncomment the two lines in the virtual host configuration file that set accesslog.filename and server.errorlog. The template will create those two log files in a subdirectory of /var/log/lighttpd named after the hostname of the virtual host. If that's what you want, you need to create that subdirectory and make sure the lighttpd user can write to it.
mkdir -p /var/log/lighttpd/subdomain.example.com
chown lighttpd:lighttpd /var/log/lighttpd/subdomain.example.comAlternatively, change the last slash to an underscore, to log to files within /var/log/lighttpd itself:
accesslog.filename = log_root + "/" + server_name + "_access.log"
server.errorlog = log_root + "/" + server_name + "_error.log"Running PHP
If you want to run a php website within lighttpd, you first need to install php itself. If you just install the php package with dnf, it will install a copy of apache too, which we obviously don’t need. However this is a “weak dependency” of the php package, so we just install without that:
sudo dnf install --setopt=install_weak_deps=False php php-cliIf you want to install a specific version of PHP, rather than the LTS version provided by the package manager for the life of your AlmaLinux (etc.) version, that can be done, but it’s beyond the scope of this blog post. That’s a PHP question, not a lighttpd configuration question. (Hint: Either compile from source, or use the remi repositories)
In 2019, we used the fastcgi implementation that comes with lighttpd. This does not now ship with lighttpd, so we need to install it:
sudo dnf install lighttpd-fastcgiNow if you look in /etc/lighttpd/conf.d you’ll see a file named fastcgi.conf that wasn’t there a moment ago. You need to do two things.
- Tell lighttpd to load the fastcgi module. Edit /etc/lighttpd/modules.conf, and uncomment the line
include conf_dir + "/conf.d/fastcgi.conf". - Tell lighttpd’s now loaded fastcgi module how to connect to PHP over fastcgi. This is the same directive we needed back in 2019. Edit
/etc/lighttpd/conf.d/fastcgi.conf. For tidiness, find the lines that give a sample PHP configuration for fastcgi, and paste the following lines into that section of the file:
fastcgi.server = ( ".php" => ((
"bin-path" => "/usr/bin/php-cgi",
"socket" => "/tmp/php.socket"
)))You should then be able to restart lighttpd (sudo systemctl restart lighttpd), put a file called info.php in /var/www/lighttpd or the public_html directory for the virtual host you just created . Load {your-domain}/info.php in a browser, and you should see the phpinfo() page.
PHP over FPM
These days, few people run PHP over cgi, because it's faster (especially when caching) to run a persistent php-fpm daemon and connect to that. What we just did is not wasted, because you still need to use fastcgi to connect lighttpd to your php-fpm process.
First, install php-fpm: sudo dnf install php-fpm.
Then, we need to configure it. You’ll find there's a configuration file named /etc/php-fpm.d/www.conf. Edit this file. Find the two lines that set user = apache and group = apache; change to user = lighttpd and group = lighttpd. In theory, php-fpm can open a unix socket and lighttpd can use that to connect, but that can be fiddly with permissions. So the easier route is to open a network port (that only accepts connections from the same server). Change the line listen = /run/php-fpm/www.sock into listen = 127.0.0.1:9000. (127.0.0.1 ensures the port isn’t open to the whole internet; 9000 is the standard TCP port used for php-fpm).
Then you can start up the php-fpm process (sudo systemctl enable --now php-fpm; systemctl status php-fpm to check)
Then we need to modify lighttpd’s fastcgi configuration so that it uses php-fpm not cgi. Edit /etc/lighttpd/conf.d/fastcgi.conf again. Earlier, we told it to connect to php with these lines:
fastcgi.server = ( ".php" => ((
"bin-path" => "/usr/bin/php-cgi",
"socket" => "/tmp/php.socket"
)))Replace with these new ones:
fastcgi.server = ( ".php" => ((
"host" => "127.0.0.1",
"port" => "9000"
)))Restart lighttpd (sudo systemctl restart lighttpd) and your info.php page should show that it connects using php-fpm.
There’s one last selinux gotcha. You may need to set setsebool -P httpd_can_network_connect 1 to allow lighttpd to connect to a network socket.
Other things you might do
That’s pretty much it.
Sure, there are other things you might wish to do. You might want to install mariadb and tell PHP how to connect to it (covered in the 2019 posts referenced above). You might want to enable mod_redirect so you can write rewrite rules in your configuration. I’ll leave you to research these as you need them.
For now, this guide should get you up and running using lighttpd on a RHEL-derived server, with multiple virtual hosts, and running PHP if you wish.
Any questions or other tips, please leave in the comments.
Recent comments