How to install Wordpress with SSL on a new Ubuntu server

There are probably thousands of guides on this, but I was looking for the basic steps and couldn't find anything I would be happy sending someone, so here's my take on it.

Aside from the operating system itself, three parts are needed to serve a Wordpress site:

  • PHP, to run the Wordpress application itself;
  • MySQL, as a database to store Wordpress data such as posts and user accounts;
  • A webserver, which takes requests from your website visitors (which pages they want to see), passes them to Wordpress, and returns the result to the user for their browser to display. The most common webservers are Apache and Nginx, in this guide I'll use Apache.

Additionally, we'll request and install a free SSL certificate for the website. For this, a domain name (or subdomain) will be needed.

I'm going to use the example myblog.com for the instructions, you'll need to substitute with your own domain name.

Also, this guide assumes you have configured DNS to point the A (and/or AAAA) record to your server. If you haven't, you can add the following entry to your hosts file and it will work for you (but the SSL section will not work):

11.22.33.44 myblog.com - replace 11.22.33.44 with the IP address of your server. If you don't know it, curl ipv4.icanhazip.com from the server.

Install the packages we need

We've determined we'll need PHP, MySQL, and Apache to run Wordpress. So let's start by installing them. Login as root (if you want to use sudo install, make sure it's configured for your user and prefix every command with sudo).

apt-get update
apt-get install php mysql-server apache2

Let's also install a couple of other libraries we will need or are recommended by Wordpress:

apt-get install pwgen unzip php-mysqli php-curl php-dom php-mbstring

Download Wordpress

This is usually the last step in guides, which I think is fine if you already know how it all fits together, but I want to take a more bottom-up approach here.

To download Wordpress, we first need to decide where to put it. Each website (in a basic sense) lives in a directory, which Apache calls the DocumentRoot. This is where Apache looks for files, so for example if it sees a request for myblog.com/index.php by default will look for a file called index.php in the DocumentRoot (directory) specified for myblog.com.

Let's say that the website will be in /var/www/myblog.com/ and so we'll download Wordpress there. First we'll create the directory, then we'll download Wordpress as a zip file into a temporary directory, unzip it, and move it in the new directory:

mkdir /var/www/myblog
cd /tmp/
wget https://wordpress.org/latest.zip
mv wordpress/* /var/www/myblog.com/

At this point, Wordpress is installed, but not connected to a website or a database.

Connect Wordpress to Apache

Apache is designed to handle multiple websites on the same server. A lot of the guides tell you to install Wordpress onto the default website (/var/www/html) but we're not going to do this.

The main configuration file is in /etc/apache2/apache2.conf, and it includes any websites (Virtual Hosts) defined in /etc/apache2/sites-enabled as long as they end with .conf. Each config file in sites-enabled should be a symbolic link (shortcut) to the real config file in /etc/apache2/sites-available so we're going to do that.

Use your favourite text editor (or just cat) to create /etc/apache2/sites-available/myblog.com.conf with the following content:

<VirtualHost *:80>
	ServerName myblog.com
	ServerAlias www.myblog.com

	ServerAdmin you@youremail.com
	DocumentRoot /var/www/myblog.com

    <Directory /var/www/myblog.com>
		AllowOverride all
		Options -Indexes
    </Directory>
    
	# For the logs you can use eg. ${APACHE_LOG_DIR}/myblog/ instead but make sure to create the directory first.
	ErrorLog ${APACHE_LOG_DIR}/myblog-error.log
	CustomLog ${APACHE_LOG_DIR}/myblog-access.log combined
</VirtualHost>
/etc/apache2/sites-available/myblog.com.conf

Next tell Apache to enable this website and allow rewrite rules:

a2enmod rewrite
a2ensite myblog.com.conf
systemctl reload apache2

Visit your website, and you should see Wordpress is installed, but is asking for database details:

Configuring MySQL

Here we need to configure three things. A database for Wordpress to use, and a user for Wordpress to connect to MySQL with, and access for that user. Use a strong password here, I use the command pwgen 20 and choose a password from there.

Run mysql to login:

mysql

Next, in the MySQL database, create the database,

mysql
CREATE DATABASE myblog;
CREATE USER 'wp_myblog'@'localhost' IDENTIFIED BY 'STRONGPASSWORDHERE';
GRANT ALL ON myblog.* TO 'wp_myblog'@'localhost';
exit;

Ignore all the guides telling you that you need to flush privileges, you don't if you use CREATE USER and/or GRANT. Now, we need to tell Wordpress to use that database.

At the moment, the user Apache is running as (www-data) can read all of the Wordpress files it needs but it cannot create or edit files. This is more secure, but means that Wordpress cannot setup the config file itself. So, we'll temporarily allow Apache to write files for Wordpress

chown -R www-data:www-data /var/www/myblog.com

Next to back to the website and click "Let's Go".

Fill in the database connection details you submitted above and click Submit:

Click Run the installation, choose a name for your blog, a username and password for logging into Wordpress, and enter your email address. You should now be able to login to Wordpress.

We now need to fix the permissions. I'm putting root here, but it would be better to create a user just for the site. I won't cover that here.

These are basic security recommendations but will stop Wordpress updating itself. You should do your own research on Wordpress permissions, these are the strictest permissions I've found that will still allow plugins to be installed.

chown -R root:root /var/www/myblog.com
chown -R www-data:www-data /var/www/myblog.com/wp-content
chown -R www-data:www-data /var/www/myblog.com/wp-admin
chgrp www-data /var/www/myblog.com
chmod 750 /var/www/myblog.com

SSL Certificate

Finally, we'll setup SSL. We need to request a certificate and then tell Apache to use it for HTTPS connections.

Request the certificate

We'll use acme.sh for this, the instructions are here but the following two commands should work for you:

curl https://get.acme.sh | sh -s email=my@example.com
/root/.acme.sh/acme.sh --issue -d myblog.com -d  www.myblog.com --webroot /var/www/myblog.com/ --server letsencrypt

Once it runs you should see something like:

[Mon Sep  9 11:00:42 AM CEST 2024] Success
(text redacted)
[Mon Sep  9 11:00:44 AM CEST 2024] Your cert is in: /root/.acme.sh/myblog.com_ecc/myblog.com.cer
[Mon Sep  9 11:00:44 AM CEST 2024] Your cert key is in: /root/.acme.sh/myblog.com_ecc/myblog.com.key
[Mon Sep  9 11:00:44 AM CEST 2024] The intermediate CA cert is in: /root/.acme.sh/myblog.com/ca.cer
[Mon Sep  9 11:00:44 AM CEST 2024] And the full chain certs is there: /root/.acme.sh/myblog.com_ecc/fullchain.cer

We are interested in the cert key, and the full chain certs.

Configure Apache to use SSL

Now we need to configure Apache to use this certificate to encrypt and decrypt HTTPS connections (on port 443) for this website, using the above certificate and key.

First, we need to enable SSL in Apache:

a2enmod ssl - it will tell you to restart Apache, we'll do that in a minute.

Next re-open /etc/apache2/sites-available/myblog.com.conf and below the previous config add a new VirtualHost:

<VirtualHost *:443>
	ServerName myblog.com
	ServerAlias www.myblog.com

	ServerAdmin you@youremail.com
	DocumentRoot /var/www/myblog.com

	<Directory /var/www/myblog.com>
		AllowOverride all
		Options -Indexes
	</Directory>
    
	ErrorLog ${APACHE_LOG_DIR}/myblog-error.log
	CustomLog ${APACHE_LOG_DIR}/myblog-access.log combined

	SSLEngine on
	SSLCertificateFile /root/.acme.sh/myblog.com_ecc/fullchain.cer
	SSLCertificateKeyFile /root/.acme.sh/myblog.com_ecc/myblog.com.key

</VirtualHost>

Ask Apache to double check the config to make sure you haven't made a mistake, and if it's ok, restart Apache

apache2ctl configtest   - it should say Syntax OK

systemctl reload apache2

Now visit https://myblog.com and it should work. If it redirects to something (eg. https://YOURIPADDRESS) you should be able to fix it by adding the following two lines to '/var/www/myblog.com/wp-config.php' just above DB_NAME:

define( 'WP_HOME', 'https://myblog.com' );
define( 'WP_SITEURL', 'https://myblog.com' );

That's it. Happy blogging.