Mastodon

Creating randomly-selected 404 pages

September 20, 2023

Tags: Linux Tutorial

One of the artists I listen to, Emma Essex, has this cool thing on their website where the 404 page is a different one every time. I thought to myself that this can be done simply and it surprisingly can be done pretty easily.

All it requires is a cgi-bin script, a folder full of HTML files, and some extra stuff in nginx.

Initializing

I'll be using /var/www/html as the root folder for all my stuff, and I'll be using nginx on Rocky Linux 9.x.

First off, create 2 directories in your /var/www/html folder;

  • cgi-bin
    • Folder where our script will go
  • 404_pages
    • Directory where pages will be randomly selected.

I've made 2 files in the 404_pages directory.

simple.html

<h1>404, page not found.</h1>
<p>The page that you're looking for doesn't exist.</p>

and simple_alt.html

<h1>404, page not found</h1>
<p>w-wha! the page that you're looking for doesn't exist ;w;</p>

I've also made a script in the cgi-bin directory with the name of rand.sh.

#!/bin/bash
item=$(find /var/www/html/404_pages -type f | shuf -n 1)
echo "Content-Type: text/html"
echo ""
cat $item

Make sure that the rand.sh file has the correct permissions

sudo chown nginx:nginx /var/www/html/cgi-bin/rand.sh
sudo chmod 777 /var/www/html/cgi-bin/rand.sh

You also need to install the fcgiwrap package.

# debian-based
sudo apt install fcgiwrap
# rhel-based
sudo dnf install fcgiwrap

Adding stuff in the nginx config

For the fcgi stuff to work, you need to add a couple of things to your nginx config.

Here's a basic config that creates a new route at /404 which then runs the fgci script result, and it will replace the 404 route with the output contents of the fcgi script.

server {
        listen 80;
        server_name _;
        root /var/www/html/;
        index index.html;

        error_page 404 /404;

        location = /404 {
                gzip off;
                include fastcgi_params;
                fastcgi_pass unix:/var/run/fcgiwrap.sock;
                fastcgi_param SCRIPT_FILENAME /var/www/html/cgi-bin/rand.sh;
        }
}

Extra scripts to make things work properly

With the way that fcgiwrap is configured, there are a couple of things you need to do in order to get it working.

Create the file /etc/systemd/system/fcgiwrap.service with the following content

[Unit]
Description=FastCGIWrap Service
After=network-online.target

[Service]
Type=exec
ExecStart=/usr/sbin/fcgiwrap
User=nginx
Group=nginx

[Install]
WantedBy=multi-user.target

Also create the file /etc/systemd/system/fcgiwrap.socket with the following content

[Unit]
Description=FastCGIWrap Socket

[Socket]
ListenStream=/run/fcgiwrap.sock
User=nginx
Group=nginx

[Install]
WantedBy=sockets.target

You also need to create a service which sets the correct permissions for the /run/fcgiwrap.sock file

Create the script at /root/fcgi-init.sh and make sure it's chmod is 777

#!/bin/bash
chown nginx:nginx -R /var/run/fcgiwrap.sock
chmod 777 /var/run/fcgiwrap.sock

And create the service at the location /etc/systemd/system/fcgi-init.service

[Unit]
Description=Initialize FCGIWrap Service
After=network-online.target

[Service]
Type=exec
ExecStart=/root/fcgi-init.sh
User=root
Group=root

[Install]
WantedBy=multi-user.target

Finalizing

Make sure that all the services are running and enabled

systemctl enable --now fcgiwrap.socket
systemctl enable --now fcgiwrap.service
systemctl enable --now fcgi-init.service

Now when you try and load a page that doesn't exist, it should use one of the files in /var/www/html/404_pages.

screen recoding demoing the random 404 pages