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


HTTP/2 is the latest version of the HTTP protocol, which is the communication protocol used whenever you interact with a website. HTTP/2 can provide a significant improvement to the load time of your sites. I wrote a complete article on HTTP/2, which explains the benefits in more detail. In short, there really is no reason not to enable HTTP/2, the only requirement is that the site must also be served over 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 over the last couple of years. This is partly due to Google announcing it as a factor in ranking your website in search results and also due to the introduction of Let’s Encrypt, which provides free SSL certificates.

Obtaining an SSL Certificate

Before obtaining an SSL certificate you will need to ensure that you’ve added an A record to your DNS provider that points to the IP address of your server.

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 -d

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/
Key is saved at: /etc/letsencrypt/live/

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

sudo certbot renew --dry-run

Nginx Server Block

Now we need to set up a server block so that Nginx knows how to deal with requests. 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 installed Nginx you may remember we created a file in the /var/www/html directory. This was because this is the default document root that Nginx configures. However, we want to make use of a more manageable directory structure for our WordPress configuration.

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:

Directory Structure

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
chmod -R 755

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

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 http2;
    listen [::]:443 ssl http2;


    ssl_certificate /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;

    access_log /home/ashley/;
    error_log /home/ashley/;

    root /home/ashley/;
    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.0-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;

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


    ssl_certificate /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;

    return 301$request_uri;

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


    return 301$request_uri;

Download the complete set of Nginx config files

This is a bare-bones server block that informs Nginx to serve the domain over HTTPS. The www subdomain will be redirected to 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 simply adding or removing a symbolic link (or symlink, think of it as a shortcut) 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/ /etc/nginx/sites-enabled/

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
ashley@pluto:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
ashley@pluto:~$ sudo service nginx restart
* Restarting nginx                                                           [ OK ]

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

Creating 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 MySQL.

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

Copyright (c) 2000, 2022, 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

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


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

CREATE DATABASE ashleyrich_com CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;

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

CREATE USER 'username'@'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 ashleyrich_com.* TO 'username'@'localhost';

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

GRANT SELECT, INSERT, UPDATE, DELETE ON ashleyrich_com.* TO 'username'@'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:


Finally, you can exit MySQL:


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

Installing WordPress

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

Start by navigating to the site’s public directory:

cd ~/

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=ashleyrich_com --dbuser=username --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 --url= --title='Ashley Rich' --admin_user=ashley --admin_password=password

You should see the following message:

sh: 1: /usr/sbin/sendmail: not found
Success: WordPress installed successfully.

You can safely ignore the sendmail not found error. This occurs because we haven’t set up email sending yet. We’ll set up email sending in chapter 5.

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

Blank WordPress Installation

Adding 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 database and MySQL user.
  7. Navigate to the site’s public directory and download, configure and install WordPress using WP-CLI.

You are free to add as many additional 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 package. Caching will also greatly reduce system resource usage, which is something I will guide you through in the next chapter.