Simply Testable Blog

Figuring out how to automate away the pain of routine front-end web testing; the story behind SimplyTestable.com.

211 posts covering the initial idea, growth of the service, features, advances, failures and successes.

Introduction

I recently finished setting up some of the most important Simply Testable web services to use SSL (or HTTPS if you prefer).

This means that the following domains are now all being served over SSL:

Sidenote1: I haven't yet set up SSL for app.simplytestable.com as I didn't quite set up the correct subject alternative names for the secure certificate and at present my secure certificate doesn't cover app.simplytestable.com.

Sidenote2: I couldn't secure help.simplytestable.com and blog.simplytestable.com as these static sites are hosted on GitHub Pages which can't support SSL.

Here's a quick run through of how I went about choosing the correct type of secure certificate and then setting up Nginx to use a single certificate to for multiple related domains on the same host.

Choosing the type of certificate to use

It's been a while since I bought a secure certificate. When I last did so there were two options to consider if you wanted to secure more than one hostname under the same domain:

  • for N hostnames, get N single-domain secure certificates

  • get a single wildcard secure certificate covering *.example.com

There's now a third option thanks to the the subject alternative name extension to X.509.

This extension allows us to opt for a single secure certifcate that is valid for a collection of listed hostnames. This is called a unified communications certificate (UCC).

From examining various secure certificate providers I noticed that a UCC secure certificate covering a small collection of domains is significantly cheaper than a wildcard certificate (and significantly significantly cheaper than multiple single-domain certificates).

As I have just one primary domain and three subdomains to cover (that's four in total), I opted for a multiple-domain (UCC) secure certificate from GoDaddy as it supports up to five related domains.

Installing

  1. Pop your secure certificate on your server

    You will have received a .crt file from your signing authority. This is your secure certificate.

    Go ahead and pop this on your server. The location doesn't particularly matter so long as it is readable by Nginx.

    For convenience, I chose the same location I used when generating the certificate signing request.

    You'll also need to pop your secure certificate's .key file on your server too (this would have been created at the time you generated a certificate signing request). This also can go anywhere but popping it in the same location as your .crt file will make life easier.

  2. Create your certificate chain (optional)

    You may have to create your certificate chain.

    Whether you need to depends on whether the signing authority from which you purchased your secure certificate has used an intermediate certificate that is not present in the certificate base of well-known trusted certificate authorities which is distributed with a particular browser.

    There's no harm in chaining your own secure certificate with an intermediate certificate. If you were given an intermediate certificate you should probably chain it with yours:

    Here's how I did that:

    cat simplytestable.com.crt gd_bundle.crt > simplytestable.com.chained.crt
  3. Reference the secure certificate and key at the http configuration level

    Most day to day Nginx configuration changes occur at the server configuration level. That's at the vhost level in Apache speak.

    We want to go one level up to from the server configuration level to the http configuration level so that the common secure certificate is available, when required, by all servers (or vhosts).

    The default Nginx configuration will include all .conf files found in /etc/nginx/conf.d/. These are interpretted at the http configuration level which is exactly what we need.

    I added the following in /etc/nginx/conf.d/ssl.conf:

    ssl_certificate     /home/simplytestable/ssl/simplytestable.com.chained.crt;
    ssl_certificate_key /home/simplytestable/ssl/simplytestable.com.key;
  4. Update your server (vhost) to turn on SSL

    With the secure certificate referenced at the http level, the only change you need to make at the server level is to listen on the correct port and turn SSL on:

    server {
    # ...
      listen 443 ssl;
      ssl on;
    # ..
    }

Additional bonus configuration

The above will get your single UCC secure certificate working with multiple domains on the same host on the same IP.

There are a handful of additional configuration changes I made.

These are not absolutely essential or are specific to running a PHP application under Nginx and php-fpm and so they live here in the bonus additional configuration section.

  1. Going full SSL (HTTPS only and no HTTP)

    If you really want to ensure traffic between clients and your web server is encrypted and if you really want to prevent man-in-the-middle attacks (and why would you be using SSL otherwise?), you really want to ensure everything goes over HTTPS.

    We can do this by using two (instead of one) server record per site.

    One of the server server records is pretty much as it was before but operates on HTTPS only. The other exists solely to redirect HTTP traffic to HTTPS.

    You can probably achieve the same with just one server record but the resulting configuration will likely be overly complex.

    In general, replace this:

    server {
        listen 80;
        listen 443 ssl;
        server_name example.com;
    
        # ...
    }

    with this:

    server {
        listen 80;
        server_name example.com;
        rewrite ^ https://$server_name$request_uri?;
    }
    
    server {
        listen 443 ssl;
        server_name example.com;
    
        # ...
    }
  2. HTTPS the correct way with php-fpm

    If you're running a PHP application under php-fpm you're probably using fastcgi_param directives to pass environment variables to PHP.

    Here's a cut-down server record I was previously using for a Symfony2-based application:

    server {
        # ... 
    
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ ^/(app|app_dev)\.php(/|$) {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_split_path_info ^(.+\.php)(/.*)$;
            include fastcgi_params;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param  HTTPS off;
        }
    
        # ... 
    }

    Notice the fastcgi_param HTTPS off line? This sets the _$SERVER['HTTPS'] environment variable in PHP.

    If you're serving your site over HTTPS and have the above (incorrect) configuration, you're likely to encounter your HTTPS traffic being interpretted at the application level as being within an HTTP (not HTTPS) environment.

    This can cause some interesting problems, such as HTTP to HTTPS to HTTP to HTTPs redirect loops or Nginx refusing to handle a plain HTTP request being sent to a HTTPS url. Neither is a good thing.

    This is the more correct (i.e. doesn't break the entire application) configuration as based on the above example:

    server {
        # ... 
    
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ ^/(app|app_dev)\.php(/|$) {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_split_path_info ^(.+\.php)(/.*)$;
            include fastcgi_params;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param  HTTPS on;
        }
    
        # ... 
    }

It's all quite simple really

Setting up Nginx to use a UCC secure certificate covering multiple domains on the same host is dead simple.

I covered what I did above in some detail, but in essence all you need to do is:

  1. Pop your .crt and .key files somewhere safe on your host in a location that is readable by Nginx.

  2. Reference these at the http level in the Nginx configuration.

  3. Set your server record to list on port 443 and turn on SSL.

  4. Reload the nginx configuration: sudo service nginx reload