Let's Encrypt has been in the news recently for launching their public beta. This means that now pretty much anyone can quickly and easily get a public key certificate for their server—all for free! This is a great step towards a safer and more secure web for all.

The letsencrypt-auto client provided by Let's Encrypt[1] has the goal of fully automating the setup process, but unfortunately at the time of writing the nginx support is very experimental, so we'll have to do some manual configuration to get HTTPS setup on our site.


Note: Since writing this post, I've started using CloudFlare. CloudFlare uses the SNI extension to TLS to secure traffic between their servers and you, the end user. This means the certificate your browser sees is CloudFlare's rather than the Let's Encrypt certs described in this post. However, the original Let's Encrypt certs are still used to secure the connection between this server and CloudFlare. CloudFlare calls this "Full SSL (strict)".


Getting the certs

Thankfully, we can still use letsencrypt-auto to automatically get our certificates. All we need to do is run the following on our server:

$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt
$ sudo service nginx stop # Will bring down the site temporarily!
$ ./letsencrypt-auto certonly --standalone -d lyall.co -d www.lyall.co
$ sudo service nginx start # OK we're back online

Note: I'm using this site as the example, so you'll need to change urls/paths as appropriate.

OK, so now we have our certificates! Mine are located in /etc/letsencrypt/live/lyall.co/. This is only half the battle though, we still need to tell nginx to use the certificates to secure connections to the site.

Configuring nginx

If you're unsure about what settings you should use, Mozilla has a great server configuration generator you can use. My settings mirror the modern nginx v1.8.0 with openssl 1.0.1 and HSTS enabled configuration. Less "modern" configurations are less secure, but are more compatible with older browsers and OSes.

Before making the config changes, we need to use openssl to generate our dhparams file.[2] In the /etc/nginx directory we run the following:

$ sudo openssl dhparam -out dhparam.pem 2048

Now add the necessary lines to your nginx config file (usually located in /etc/nginx or /etc/nginx/conf.d/). Mine now looks like the following:

server {
    listen 443 ssl default_server;

    ssl_certificate /etc/letsencrypt/live/lyall.co/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/lyall.co/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;


    # Diffie-Hellman parameter for DHE ciphersuites
    ssl_dhparam /etc/nginx/dhparam.pem;

    # Mozilla's modern configuration
    ssl_protocols TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
    ssl_prefer_server_ciphers on;

    # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
    add_header Strict-Transport-Security max-age=15768000;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/lyall.co/fullchain.pem;

    ...
}
...

Note how we're using listen 443 ssl instead of the old listen 80. You can keep both if you want to accept both encrypted and unencrypted connections, or do what I did and add a server block to your config to redirect http requests to https like so:

server {
        listen 80;
        server_name lyall.co;
        return 301 https://lyall.co$request_uri;
}

Last steps

On my server, iptables was set up to drop all requests that weren't for HTTP (port 80) or ssh (port 22). So we need to add a rule that allows HTTPS requests (port 443). We can do that with the following command:

$ sudo iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT

However, since iptables evaluates rules top down (in the order they were added), our drop all packets rule will be hit before our new accept 443 rule, so we need to move the drop rule to the end of the list. We can do this by removing it then re-adding it:

$ sudo iptables -D INPUT -j DROP
$ sudo iptables -A INPUT -j DROP

Now we have to tell iptables to save these rules. I'm using iptables-persistent on Ubuntu 12.04 LTS, so your save command may vary depending on your setup.

$ sudo service iptables-persistent save

All that's left to do now is update nginx with our new config:

$ sudo nginx -s reload

Testing our setup

If everything worked right, if we navigate to our site at http://lyall.co it should redirect us to https://lyall.co and show us that we have a secure connection with the coveted green lock icon:

HTTPS connection success

For further testing you can run the SSL Labs SSL Server Test against your site, and if all goes well you'll see the following:

SSL Labs test success


And that's it! Your site is now secure against man-in-the-middle attacks[3] and you're helping the privacy and security of your users. You might even get improved search rankings because of it.[4]


  1. letsencrypt GitHub ↩︎

  2. Why do we generate dhparams? ↩︎

  3. Probably ↩︎

  4. HTTPS as a ranking signal ↩︎