Dev-ops

How to Add Letsencrypt SSL Certificate to WordPress

https ssl

With more and more browsers showing warning messages on non-HTTPS sites, more people are opting for SSL certificates. Without SSL, traffic between the browser and the destination server are susceptible to being sniffed. SSL guards against sniffing. It also lets users make sure the login credentials are sent to the identity that they intend to. That is to say, when you enter username and password on a SSL encrypted page, you can be sure your data is not only encrypted (which prevents sniffing) but also the data would only reach the entity to whom SSL certificate is issued.

There are currently three ways one can create and implement SSL.

  1. One can self-sign a certificate using tools like openssl. This enables sites to encrypt data between client (i.e. browser) and its servers. But self-signed certificates do not authenticate the owner of the certificate’s identity. This is because self-signed certificates are created and implemented by the owner of the site — without a middleman to confirm the owner’s identity. Self-signed certificates do not cost anything and they are best used in local development servers, where certificate owner’s identity does not need to be authenticated. (For local development, mkcert is a better and painless choice.)
  2. One can purchase SSL certificates from Certification Authorities, who perform verification of the certificate owner. Due to the existence of a third-party that the user can trust, these certificates are more secure. SSL certificate from a CA can be very expensive.
  3. There is yet another way to sign and implement SSL on your sites — using LetsEncrypt, a non-profit organization founded by EFF, Mozilla, Cisco among others. LetsEncrypt aims to make encrypted connections a standard on all websites without any complex mechanism to sign and renew certificates. LetsEncrypt certificates do not cost anything and several tools are there to issue certificates from LetsEncrypt which makes the whole process a breeze.

In this post, we would discuss about how to issue, renew and install LetsEncrypt certificates on a WordPress site. For the sake of this post, I assume:

  1. You have a Linux server with ssh and root access.
  2. You are running WordPress with Apache2.

Installation

There are several tools that let users issue and install LetEncrypt certificates. Based on our experience, acme.sh is the easiest to install and manage certificates. acme.sh is a shell script that can be run from most Linux distributions. To install, log into your server with root privilege and run the following command:

curl https://get.acme.sh | sh

Running curl (or wget) from shell and executing remote commands can be a security concern for a lot of people. You can audit the code here (and here) before running the above command. Once installed, the package can be run with acme.sh.

Issuing Certificates

To issue a certificate, just run:

acme.sh --issue -d example.com -w /var/www/example.com/public_html

Change example.com with the actual domain name for which certificate is to be issued. Also, in the above command, it is assumed that the document root for example.com is /var/www/example.com/public_html. Change this to the actual directory address.

Once you run the above command, acme.sh will send certificate issue request to LetsEncrypt’s website and create a file at the document root address. This file subsequently gets checked by LetsEncrypt to verify the ownership of the domain.

Other than placing files at document root and verifying, there are several other verification mechanisms acme.sh offers. These are not in the scope of this post but you can check them here.

Installing the certificates

Once LetsEncrypt issues and signs certificates, you need to install them. By installing, we mean that the certificate files are placed in directories where the Apache can access them while serving the site. To install the certificates:

acme.sh --install-cert -d example.com \
--cert-file      /path/to/certfile/in/apache/cert.pem  \
--key-file       /path/to/keyfile/in/apache/key.pem  \
--fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
--reloadcmd 'service apache2 force-reload'

Here, the certificates are installed in the path mentioned the command. I personally prefer the certificates to be placed in /etc/apache2/ssl/domain/ directory where domain is the actual domain name. The command above reloads Apache server once the installation is complete.

It is worthwhile to mention that simply copying the files to the directories (with cp)is not advised. Behind the scene, acme.sh does several tasks when it is called with --install-cert

To automate the renewal procedures for the certificates, you can create a cronjob with:

0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null

 

Configuration for WordPress

Now that we have a signed certificate, we need to serve the site over SSL. Merely issuing and installing the certificates won’t have any change to the sites if not configured properly. First, we need to change the settings in WordPress. Go to WordPress admin panel’s Setting page. There, change “WordPress Address” and “Site Address”. Add https.
After that, go to Settings > Permalinks and save the settings. This is re-create .htaccess if at all any change is required.

Configuration for Apache

Now that we are finished with WordPress configuration, it’s time to move to Apache. Open /etc/apache2/sites-enabled/example.com.conf in your favorite text editor. You may already have:

<VirtualHost *:80>
        ServerAdmin [email protected]
        ServerName example.com
        ServerAlias example.com
        DocumentRoot /var/www/example.com/public_html

        <Directory /var/www/example.com/public_html>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

Just copy the above code and paste it at the end of the file. Next, just change <VirtualHost *:80> to <VirtualHost *:443> in the last code block. Add the following lines under <VirtualHost *:443>:

SSLEngine on
SSLCertificateFile /etc/apache2/ssl/example.com/fullchain.pem
SSLCertificateKeyFile /etc/apache2/ssl/example.com/key.pem

It is important to note here that if you are using Apache version below 2.4, your configuration will be different.

SSLEngine on
SSLCertificateFile /etc/apache2/ssl/example.com/cert.pem
SSLCertificateKeyFile /etc/apache2/ssl/example.com/key.pem
SSLCertificateChainFile /etc/apache2/ssl/example.com/fullchain.pem

Once that is done, add the following inside <VirtualHost *:80> codeblock:

Redirect / https://example.com/
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]

So, finally, example.com.conf file should look something like this:

<VirtualHost *:80>
        ServerAdmin [email protected]
        ServerName example.com
        ServerAlias example.com
        DocumentRoot /var/www/example.com/public_html
        Redirect / https://example.com/
        RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
        <Directory /var/www/example.com/public_html>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>
<VirtualHost *:443>
        ServerAdmin [email protected]
        ServerName example.com
        ServerAlias example.com
        DocumentRoot /var/www/example.com/public_html

        <Directory /var/www/example.com/public_html>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

        SSLEngine on
        SSLCertificateFile /etc/apache2/ssl/example.com/cert.pem
        SSLCertificateKeyFile /etc/apache2/ssl/example.com/key.pem

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

Next, restart apache with: service apache2 reload. Remember to change example.com to the actual domain name.

Moving legacy content to HTTPS

We have issued, installed SSL certificate, forced pages to be served over HTTPS. But yet another problem looms large — we already have several posts which contains images in http:// urls. If we don’t fix that, browsers will show a Mixed Content warning and will block non-HTTPS content. To solve this, just go to your database manager (PHPMyadmin, may be) and run the following commands:

UPDATE wp_posts 
SET    post_content = ( Replace (post_content, 'src="http://', 'src="//') )
WHERE  Instr(post_content, 'jpeg') > 0 
        OR Instr(post_content, 'jpg') > 0 
        OR Instr(post_content, 'gif') > 0 
        OR Instr(post_content, 'png') > 0;

The above code is for replacing all occurrences of http:// with protocol agnostic //. But this replacement is for single-quoted urls. For double-quoted ones, run this:

UPDATE wp_posts 
SET   post_content = ( Replace (post_content, "src='http://", "src='//") )
WHERE  Instr(post_content, 'jpeg') > 0 
        OR Instr(post_content, 'jpg') > 0 
        OR Instr(post_content, 'gif') > 0 
        OR Instr(post_content, 'png') > 0;

 

Conclusion

The above procedure is the simplest and should fit to all regular WordPress installations. I highly recommend going through acme.sh documentation. The verification process can be further simplified if you opt for DNS API mode while issuing certificates.