In the previous chapter, I showed you how to install PHP 8.3, Nginx, WP-CLI, and MySQL on the backend, which formed the foundations of a working Linux web server & LEMP stack. In this chapter, I will guide you through the process of deploying your first HTTPS enabled WordPress site with HTTP/2 support.

HTTPS

HTTPS is an extension for HTTP/2 which secures the communication between a server and a client. It ensures that all data sent between the devices is encrypted and that only the intended recipient can decrypt it. Without HTTPS any data transmitted will be sent in plain text, allowing anyone who might be eavesdropping to read the information.

HTTPS is especially important on sites which process credit card information but has gained widespread adoption and very few sites run without it. Google also considers it a factor in ranking sites in search results.

Choose a Domain and Configure DNS

In Chapter 1, we choose a domain name for our server and set it up as a hostname by configuring the DNS to point at the IP address of our server. Now we need to do something similar for the site we’re about to set up.

I’ve chosen the domain name globex.turnipjuice.media for my site and have created a CNAME record pointing to globex.turnipjuice.media in my DNS for that domain as well as www.globex.turnipjuice.media.

Screenshot of adding CNAME to Cloudflare DNS.

It’s good practice to use CNAME records here instead of A records so that if you ever need to update the IP address of your server in the future, you only need to update one record.

Obtain an SSL Certificate

Now let’s install Certbot, the free, open source tool for managing Let’s Encrypt certificates:

sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update
sudo apt install certbot python3-certbot-nginx

To obtain a certificate, you can now use the Nginx Certbot plugin, by issuing the following command. The certificate can cover multiple domains (100 maximum) by appending additional d flags.

sudo certbot --nginx certonly -d globex.turnipjuice.media -d www.globex.turnipjuice.media

After entering your email address and agreeing to the terms and conditions, the Certbot client will generate the requested certificate. Make a note of where the certificate file fullchain.pem and key file privkey.pem are created, as you will need them later.

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/globex.turnipjuice.media/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/globex.turnipjuice.media/privkey.pem

Certbot will handle renewing all your certificates automatically, but you can test automatic renewals with the following command:

sudo certbot renew --dry-run

Add an Nginx Configuration for the Site

Now we need to set up a server block so that Nginx knows how to deal with requests for these domains. By default, our Nginx configuration will drop any connections it receives, as in the previous chapter you created a catch-all server block. This ensures that the server only handles traffic to domain names that you explicitly define.

When we went through the process to install Nginx you may remember we created a php.info file in the /var/www/html directory. This was because this is the default document root that Nginx configures. However, we want a more manageable directory structure for our WordPress sites.

If you’re not already there, navigate to your home directory.

cd ~/

For simplicity’s sake, all of the sites that you host are going to be located in your home directory and have the following structure:

abe@pluto:~$ ls -l ~/globex.turnipjuice.media/
total 8
drwxr-xr-x 2 abe abe 4096 Apr  6 14:02 logs
drwxr-xr-x 2 abe abe 4096 Apr  6 14:02 public

The logs directory is where the Nginx access and error logs will be stored, and the public directory will be the site’s root directory, which will be publicly accessible.

Begin by creating the required directories and setting the correct permissions:

mkdir -p globex.turnipjuice.media/logs globex.turnipjuice.media/public
chmod -R 755 globex.turnipjuice.media

With the directory structure in place it’s time to create the server block in Nginx. Navigate to the sites-available directory:

cd /etc/nginx/sites-available

Create a new file to hold the site configuration. Naming this the same as the site’s root directory will make server management easier when hosting a number of sites:

sudo nano globex.turnipjuice.media

Copy and paste the following configuration, ensuring that you change the server_name, access_log, error_log, and root directives to match your domain and file paths. You will also need to replace the file paths to the certificate and certificate key obtained in the previous step. The ssl_certificate directive should point to the fullchain.pem file, and the ssl_certificate_key directive should point to the privkey.pem file. Hit CTRL + X followed by Y to save the changes.

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;

    server_name globex.turnipjuice.media;

    ssl_certificate /etc/letsencrypt/live/globex.turnipjuice.media/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/globex.turnipjuice.media/privkey.pem;

    access_log /home/abe/globex.turnipjuice.media/logs/access.log;
    error_log /home/abe/globex.turnipjuice.media/logs/error.log;

    root /home/abe/globex.turnipjuice.media/public/;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;

    server_name www.globex.turnipjuice.media;

    ssl_certificate /etc/letsencrypt/live/globex.turnipjuice.media/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/globex.turnipjuice.media/privkey.pem;

    return 301 https://globex.turnipjuice.media$request_uri;
}

server {
    listen 80;
    listen [::]:80;

    server_name globex.turnipjuice.media www.globex.turnipjuice.media;

    return 301 https://globex.turnipjuice.media$request_uri;
}

Download the complete set of Nginx config files

This is a bare-bones server block that informs Nginx to serve the globex.turnipjuice.media domain over HTTPS. The www subdomain will be redirected to globex.turnipjuice.media and HTTP requests will be redirected to HTTPS.

The two location blocks essentially tell Nginx to pass any PHP files to PHP-FPM for interpreting. Other file types will be returned directly to the client if they exist, or passed to PHP if they don’t.

By default Nginx won’t load this configuration file. If you take a look at the nginx.conf file you created in the previous chapter, you will see the following lines:

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

Only files within the sites-enabled directory are automatically loaded. This allows you to easily enable or disable sites by adding or removing a symbolic link (or symlink) in the sites-enabled directory, linked to the configuration file in sites-available.

To enable the newly created site, symlink the file that you just created into the sites-enabled directory, using the same filename:

sudo ln -s /etc/nginx/sites-available/globex.turnipjuice.media /etc/nginx/sites-enabled/globex.turnipjuice.media

In order for the changes to take effect, you must reload Nginx. However, before doing so you should check the configuration for any errors:

sudo nginx -t

If the test fails, recheck the syntax of the new configuration file. If the test passes, reload Nginx:

sudo service nginx reload

With Nginx configured to serve the new site, it’s time to create the database so that WordPress can be installed.

Create a Database

When hosting multiple sites on a single server, it’s good practice to create a separate database and database user for each individual site. You should also lock down the user privileges so that the user only has access to the databases that they require.

Log into MySQL with the root user.

mysql -u root -p

You’ll be prompted to enter the password which you created when setting up a MySQL database.

abe@pluto:~$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 15
Server version: 8.0.37-0ubuntu0.24.04.1 (Ubuntu)

Copyright (c) 2000, 2024, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Once logged in, create the new database, replacing globex with your chosen database name:

CREATE DATABASE globex CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;

Next, create the new user using the following command, remembering to substitute globex and password with your own values:

CREATE USER 'globex'@'localhost' IDENTIFIED BY 'password';

You then need to add the required privileges. To keep things simple, you can grant all privileges but restrict them to your database only, like so:

GRANT ALL PRIVILEGES ON globex.* TO 'globex'@'localhost';

Alternatively, you can have more granular control and explicitly define the privileges the user should have:

GRANT SELECT, INSERT, UPDATE, DELETE ON globex.* TO 'globex'@'localhost';

Be careful not to overly restrict permissions. Some plugins and major WordPress updates require heightened MySQL privileges (CREATE, DROP, ALTER, etc.), therefore revoking them could have adverse effects. The WordPress Codex has more information on MySQL privileges.

For the changes to take effect you must flush the MySQL privileges table:

FLUSH PRIVILEGES;

Finally, you can exit MySQL:

exit;

Now that you have a new database, it’s time to install WordPress.

Install WordPress

You could install WordPress manually by using something like cURL or wget to download the latest.zip or latest.tar.gz archive, extract it, and then follow the WordPress installer in a web browser. But since we installed WP-CLI in the previous chapter, we’ll be using that instead.

Start by navigating to the site’s public directory:

cd ~/globex.turnipjuice.media/public

Then, using WP-CLI, download the latest stable version of WordPress into the working directory:

wp core download

You now need to create a wp-config.php file. Luckily, WP-CLI has you covered. Make sure to use the database details you set up in the previous step:

wp core config --dbname=globex --dbuser=globex --dbpass='password'

Finally, with the wp-config.php file in place, you can install WordPress and set up the admin user in one fell swoop:

wp core install --skip-email --url=https://globex.turnipjuice.media --title='Globex Corporation' --admin_user=abe --admin_email=abe@turnipjuice.media --admin_password='password'

You should see the following message:

Success: WordPress installed successfully.

You should now be able to visit the domain name in your browser and be presented with a default WordPress installation:

Screenshot of default install of a WordPress site.

Add Additional Sites

Additional sites can be added to your server using the same procedure as above and you should be able to fire up new sites within a couple of minutes. Here’s a quick breakdown of how to add additional sites:

  1. Add the relevant DNS records to the domain.
  2. Obtain an SSL certificate.
  3. Navigate to your home directory and create the required directory structure for the new site (logs and public).
  4. Navigate to the sites-available directory within Nginx and copy an existing config file for the new server block. Ensure you change the relevant directives.
  5. Symlink the config file to the sites-enabled directory to enable the site and restart Nginx
  6. Create a new WordPress database and MySQL user.
  7. Navigate to the site’s public directory and download, configure and install WordPress using WP-CLI.

You’re free to add as many sites to your server as you like, the only limiting factors are available system resources (CPU, memory, and disk space) and bandwidth restrictions imposed by your VPS provider. Both of which can be overcome by upgrading your server. Caching will also greatly reduce system resource usage, which is a tutorial that I will guide you through in the next chapter.