Last month I had to set up an SSL certificate for a news site based on WordPress.

The owner writes some of the content but there's also content that's pulled from various external sources. The last part posed some challenges. The external content contained a mix of secure and non-secure links to pages and images. Different browsers *complained* about different issues differently.

The Solution

All instructions are listed here: https://gist.github.com/lordspace/db99e4982839f16e9637de4af7ba099b

In order to keep the browsers happy and see a nice green padlock in the address bar I had to make sure all of the resources are loaded from an https location. This applies to both internal and external links.

To do that I had to capture the output very early on, do some magic with it and return it.

Initially, I have tried with a system WordPress (mu-plugin) that hooked into a content filter but not all of the content was captured. I've also tried to capture the content using ob_start() in the mu-plugin but that didn't work either so I had to use ob_start() in the wp-config.php

Correcting links

The sites that supported secure links non-secure links were corrected to the secure links. Those links were from sites such as facebook, google, youtube, blogger, feedblitz, linkedin etc. All other links were passed through a redirect script hosted on the https version of the current site (e.g. https://example.com/r.php?r=http://some-external-site.com/test

The images were also passed through that redirect script. Firefox was *ok* with that but Chrome still complained about mixed content in the Developer Console (F12). Chrome's warnings made me update the redirect script so it pulled the external images locally. The remote pulling was done once per month and images were stored in deep folder structure (wp-content/remote_images/a/b/c/site_com_image_some_hashasfasfasf) so they are quickly retrieved.

It seems the site was also outputting some binary content so the output correcting script had to check if the content contained binary data the first 120 bytes contained one of these strings: ZIP, PDF, PNG, JPG etc.

 

SSL Provider

I have kept an eye on the Let's Encrypt service for quite some time now so I have decided to give it a try.

Let's encrypt gives you a free SSL certificate which is valid for 90 days.

The client's site was on a VPS server (from linode) which I fully controlled so I wasn't going to be restricted in any way.

Let's encrypt doesn't support Wildcard SSL and probably won't in near future so you have to list all of the domains and subdomains that need to be supported. I have seen examples that allow one SSL certificate to work for multiple domains but I prefer to keep each domain's certificates separate. Of course, if you have multiple domains that point to the same location then it might make sense (e.g. example.net, example.org etc).

Let's encrypt has some (sub)commands that can generate, and even install the SSL and restart the webserver but I don't like programs to touch my server's configs as sometimes I have some custom setups. All I wanted was to have the SSL generated and I do the rest.

 

How I did it

Server Info: ubuntu 16, nginx + php7-fpm, 2GB RAM

 

Login as root

Installed let's encrypt tool

apt-get install letsencrypt

When generating the SSL you may want to do some tests with the staging environment first because if there are errors (e.g. the site can't be verified) Let's encrypt will block your requests for a week.

To use the staging servers of Let's encrypt pass --staging parameter.

 

Make sure you specify both www and non-www version of the site so the issued SSL certificate works for both variations otherwise your visitors will get an invalid SSL certificate warning.

If the webserver set up is restrictive you may have to add some rules that allow access to some folders.

You can test this by creating DOC_ROOT/.well-known/test.txt and then access it from the browser

example.com/.well-known/test.txt

 

Since the server was nginx I had to add this to the vhost.

location ^~ /.well-known {
allow all;
}


Certificate generation

letsencrypt certonly --rsa-key-size 4096 --email admin@yoursite.com --verbose --agree-tos --webroot -w /var/www/vhosts/clients/awesome-client/domain.com/www -d domain.com -d www.domain.com

 

Let's Encrypt Certificate Renewal

Let's encrypt will issue a free SSL certificate which will be valid for 90 days.

You can create a bin file and schedule a cron job to automatically get renewed. The job must be owned by root because the letsencrypt program needs to save the files in /etc/letsencrypt/live/

At the time of writing letsencrypt supports renewal of all certificates and not specific site certificates.

 

#/user/local/bin/le_renew_ssl.sh

letsencrypt renew --verbose >> /var/log/le_renew_cert.log

nginx -s reload

Let's make that file executable

chmod 0755 /user/local/bin/le_renew_ssl.sh

Add it as a regular job

crontab -e
# Will run at 12:30am on the first day of the month.
30 * * 1 * /user/local/bin/le_renew_ssl.sh