Hosting WordPress Yourself Part 6 – HTTPS and HTTP/2

Updated on Feb 22, 2017.

In the previous installment of this series, I demonstrated how to configure cron, outgoing email and automatic backups. In this post I will show you how to configure HTTPS and HTTP/2.

HTTP/2

HTTP/2 is the latest version of the HTTP protocol and can provide a significant improvement to the load time of your sites. I wrote a complete article on the subject, 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

HTTPS is a protocol for secure communication between a server (website) and a client (web browser). 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 is 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 ranking signal, but also due to the introduction of Let’s Encrypt, which provides free SSL certificates.

Obtaining and installing SSL certificates used to be an arduous task but Let’s Encrypt and the Certbot client make the process a breeze. Let’s begin…

Obtaining a Certificate

Begin by connecting to your server.

ssh ashley@pluto.ashleyrich.com

Install the Certbot client using apt-get:

sudo apt-get install letsencrypt -y

You can then obtain a certificate for your site using the letsencrypt command:

sudo letsencrypt certonly --webroot -w /home/a5hley/ashleyrich.com/public -d pluto.ashleyrich.com

This uses the webroot plugin to validate that you own the domain. The -w flag should point to your site’s public directory and the -d flag is the domain you’re requesting the certificate for. When you first run the command you should be prompted to enter your email address, which will be used for expiration notices.

On subsequent attempts you should receive the following confirmation, which details where the SSL certificate has been saved and also when it will expire. Let’s Encrypt certificates expire after 3 months:

Installing the Certificate

Open the site’s Nginx configuration file:

sudo nano /etc/nginx/sites-available/ashleyrich.com

Add the following directives within the server block, replacing the file path with the one you received when obtaining the certificate. The ssl_certificate directive should point to your fullchain.pem file and the ssl_certificate_key directive should point to your privkey.pem file:

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

ssl_certificate /etc/letsencrypt/live/pluto.ashleyrich.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pluto.ashleyrich.com/privkey.pem;

The http2 value is all that is needed to enable HTTP/2 support. Save and close the file by hitting CTRL X followed by Y. Before reloading the Nginx configuration, ensure there are no syntax errors.

sudo nginx -t

If no errors are shown, reload the configuration.

sudo service nginx reload

Now if you visit your site in the browser using HTTP you should receive an empty response. However, when visiting using HTTPS the site should successfully load and show the green pad lock icon in the address bar. If the site times out, remember to ensure that you have opened port 443 on your server firewall!

The final step is to update the WordPress address and site URL.

SSL - Image 6

Redirect HTTP Traffic to HTTPS

At the moment all HTTP traffic is dropped by the server. Instead you should redirect all HTTP traffic to HTTPS. Open the site’s virtual host file and add a new server block below the existing one:

server {
    listen 80;
    listen [::]:80;
    server_name pluto.ashleyrich.com;

    return 301 https://$server_name$request_uri;
}

Reload Nginx for the changes to take effect, but remember to test the configuration first. Now you should be redirected to HTTPS when visiting the site via HTTP.

Renewing Certificates

Certificates obtained through Let’s Encrypt automatically expire after 90 days, therefore you need to renew them often. Luckily, the Certbot client has a command which will renew all certificates which are due to expire in the next 30 days. To automate the process you can add this command to a cron job. The command will need to be executed with heightened permissions so it should be added to the root user’s crontab:

sudo crontab -e

Add the following command to the end of the file:

0 0,12 * * * letsencrypt renew >/dev/null 2>&1

This will attempt to renew certificates twice daily. Certificates which are not due to expire in the next 30 days will be skipped.

SSL Hardening

Although your site is configured to only handle HTTPS traffic it still allows the client to attempt further HTTP connections. Adding the Strict-Transport-Security header to the server response will ensure all future connections enforce HTTPS. This article by Scott Helme gives a more thorough overview.

Add the following directive to the http block.

add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";

You may be wondering why the 301 redirect is still needed if this header automatically enforces HTTPS traffic: unfortunately the header isn’t supported by IE10 and below.

Check your Nginx config and reload if successful.

sudo nginx -t
sudo service nginx reload

Now if you perform a scan using the Qualys SSL Test tool you should receive a grade A+. Not bad!

SSL - Image 8

SSL Performance

HTTPS connections are a lot more resource hungry than regular HTTP connections. This is due to the additional handshake procedure required when establishing a connection. However, it’s possible to cache the SSL session parameters, which will avoid the SSL handshake altogether for subsequent connections. Just remember that security is the name of the game, so you want clients to reauthenticate often. A happy medium of 10 minutes is usually a good starting point.

Open the main Nginx configuration file.

sudo nano /etc/nginx/nginx.conf

Add the following directives within the http block.

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

Test the Nginx configuration and reload if successful.

sudo nginx -t
sudo service nginx reload

The final nginx.conf file can be seen below, which includes everything from this series so far.

Job done! Hopefully this guide has given you a good insight into securing your sites with HTTPS. Let me know in the comments below if there are any other precautions you take.

About the Author

Ashley Rich

Ashley is a PHP and JavaScript developer with a fondness for solving complex problems with simple, elegant solutions. He also has a love affair with WordPress and learning new technologies.

  • alexhaworth

    Thanks for the great guide Ashley, do you have any rule of thumb for the opcache size based on the available ram… You’ve allocated 64mb/512mb available is that ratio fair to follow on an 8gb server? ie 1024mb opcache?

    …. running updates in the future for ubuntu nginx ect… is that worth setting to a cron job or is that deemed too risky?.. on a server setup at this level is it very rare that a server will brink from an update?

    Finally (sorry).. is there any reason you used mysql rather than mongodb?

    • I don’t really have a general rule for opcache size. I personally use a GUI like https://github.com/amnuts/opcache-gui which allows you to see exactly how much memory you’re using. You can then adjust the allocated memory accordingly.

      I’ll be covering automatic updates in a future post, but you should use the `unattended-upgrades` package. This will allow you to configure only security updates to be installed, which should never cause breaking changes.

      https://help.ubuntu.com/14.04/serverguide/automatic-updates.html

      I’m not sure WordPress will run on MongoDB out of the box, I could be wrong?

      • alexhaworth

        Thanks very much – really helpful. The mongodb was a typo sorry I meant MariaDB

        Thanks again

        • Ahh that makes sense. I actually do use MariaDB on my servers. The reason I don’t include it in this series is because I’m yet to see any benefits from using it over MySQL. Granted it has more cutting edge features but WordPress doesn’t make use of them. I’ve never benchmarked the performance difference so I can’t comment on that.

          But certainly give it a go, as it’s a drop in replacement for MySQL there are no disadvantages.

  • Really really useful, thanks Ashley!

  • are you still on the 512MB plan?

    • This is just a demo server for this tutorial series, I actually use a larger VPS for personal use. The 512MB plan will comfortably handle a hand full of small to medium traffic sites though.

  • brian.goodman

    Thank you for such a comprehensive and clear guide, especially for someone like me with no linux experience.

    Given that limited experience, is there a requirement to update nginx and the other installed components to the latest stable versions on an ongoing basis? If so, were you planning to provide some guidance on how often and how to perform those updates?

    In addition, you selected the stable version of nginx as the basis of the server install, but some recommend the mainline version since has all the latest bug fixes and support for enhanced functionality like HTTP/2.

    Is it possible to upgrade a working server from the stable version to the mainline version in order to use HTTP/2, or will I need to start from scratch or wait until the stable version has that functionality and perform a standard update?

    Thanks,

    Brian

    • Thanks for the feedback Brian.

      Both branches are acceptable, so just use whichever you feel comfortable with. The mainline branch is actually recommend by Nginx, with the caveat that new features may have further “impacts”. For that reason I choose the stable branch, which also contains all of the latest bug fixes.

      https://www.nginx.com/blog/nginx-1-6-1-7-released/

      You can switch between the stable and mainline version at anytime, just be sure to backup your Nginx configuration and virtual host files before doing so. This guide outlines the process, but you will just need to specify which branch you wish to use:

      http://leftshift.io/upgrading-nginx-to-the-latest-version-on-ubuntu-servers

  • LEMON

    This whole series of posts has been very helpful! Thanks man!

  • Marlon Amancio

    Really really really thank you Ashley!

  • Popsantiago

    Hello @A5hleyRich:disqus, i just see my subdomain pro.mydomain.com have a SSL error =/
    Do you know how i can solve it ? Thanks

    • What’s the error you’re getting?

      • Popsantiago

        I have ERR_SSL_PROTOCOL_ERROR… I’m a new be on DNS =/
        I have a A record for domain.com and http://www.domain.com and another A record for the pro.domain.com before the install like tuto i haven’t the ssl and pro.domain.com was accessible.
        Thx.

  • monoloops

    hello, cloudflare offering free SSL. And it’s really easy to setup. What you think about that?
    https://www.cloudflare.com/ssl/

  • i just tested WordPress on php7 and nginx 1.9.7 mainline version with http/2 support 😉 and result are very good 😀

  • Ziflin

    For anyone using PositiveSSL (Comodo), I was sent a zip with a website.crt and website.ca-bundle. I just concatenated the .crt with the .ca-bundle file (bundle at the bottom of the file) and this appears to make the Qualys SSL Test happy. It fixes the “incomplete certificate chain” warning.

    I also used:
    listen [::]:80 ipv6only=off;
    in the server redirect as well as:
    listen [::]:443 default_server ssl spdy ipv6only=off;
    in the main server block for ip4 and ip6 support.

  • Angel Cardenas

    exelent tutorial!!! have you tried https://letsencrypt.org/

  • Hello Ashley,
    Great tutorial!
    Can you instruct me how to install on a single server, single site the opcache-gui?
    Thank you again for your great job and efford!

  • Hello Ashley, great blog. Love it. Will you have a best practice for SSL on multisite and sub.domaines + mapped domains? I am hitting my head with SSL right now as either I get the conf wrong or the websites messed up. Any clue will be more than welcome and happy to discuss in mp for hiring. Best. Haider

  • David Swanson

    Awesome setup! Thank you! I’m having one problem. Some background. I’m using Google Cloud VM. 1 Cpu 3.5GB memory. The website is forced ssl. Using W3 Total Cache plugin. A free StarSSL certificate. Everything is working great except on the backend /wpadmin
    Front end, I can open as many pages (browser tabs at once) with no problem.
    When I’m in /wp-admin it’s quite slow. I try to open 6 products in different browser tabs at once. I can only open 5 connections at a time in wp-admin. Anything more then 5 I receive nginx 504 Gateway Time-out
    I’ve tried every fine tuning and con’t figure out what is preventing more then 5 connections at a time.
    Any help would be greatly appreciated.
    Thank you!

  • Rich J.

    Hey Ashley, appreciate the well documented tutorial. All is work fine other than when I modified for the SSL.

    I can’t get to the site correctly, css is broken and etc. The WP-Login page says ERR_TOO_MANY_REDIRECTS

    I thought it might be the wordpress site_url issue, however I modified that, tried a few things, and same issue.

    I then just decided to reinstall wordpress and use the new https:// url when doing wp core install, and still have the same issue.

    Any clues why that might be? I’ve gone over the config a few times and am using everything exactly as posted. Prior to doing any of the SSL, everything worked perfectly.

    Thanks in advance!