I'm the maker of Nomad List, Remote OK, and Hoodmaps and I experiment w/ 3D. I travel to work from anywhere, bootstrap side projects into open startups and only own what fits in my backpack. I also made 12 startups in 12 months. Before doing startups, I started a YouTube w/ 100+ mln views. Follow me on Twitter or read my blog. You can buy my book MAKE now.

Add HTTPS to NGINX for free and help make the world more secure

Update 2016/05/01: Please DO NOT follow this post as it's outdates! StartSSL is considered to be buggy and legacy these days, don't use it! It's now possible to get free certificates from LetsEncrypt.org, use them!

The recent NSA leak by Edward Snowden verified what most of us knew all along. Everything we do online and on our cellphones is eavesdropped on a massive scale. It doesn't matter if you live in America or Europe, the internet's pathway of connections is worldwide, so your traffic is bound to end up in some government's data tracking repository.

Enough has been said about it. But now let's talk practicalities. What can you do as a website or web-app developer to raise the level of security from your users? Well, You can force SSL on all connections and route them to https://. SSL certificates used to cost money, but StartSSL actually offers basic certificates for free. I'm sure there's a chance the NSA can actually break into HTTPS to, but at least it makes it harder for them to eavesdrop.

Here's how you enable SSL on your NGINX server.

Getting a free SSL certificate from StartSSL

1) First go to http://startssl.com. The interface is ridiculously badly designed, but this is the only place that offers free certificates, so I'll help you get through it.

2) Go to StartSSL's Control Panel (top-right).

3) At the bottom of the page choose the Express Lane button.

4) Enter your personal details and press continue. Unfortunately the free Class 1 SSL certificate only supports those connected to your personal details. For a certificate connected to your organization or business, you'll need to pay for a Class 2 SSL certificate. I think it's somewhat important to actually use your real details since they are checked on occasion by StartSSL's staff.

5) Copy the confirmation code from the email they sent you.

6) Now you will need to generate your private key for accessing StartSSL's website. Choose 2048 (High Grade) as that is the standard. Click Generate.

7) If you click Install, your private key will now be installed into your browser. This is rather weird, but it means you can authenticate (e.g. sign in) with StartSSL to modify your account details later. Rather interesting set up, but I guess it's more secure than just a regular sign in. This can take a few minutes. Do not refresh your browser as it will kill the entire process. Just wait and you'll see a notice when it's installed.

8) Now that your private key is installed in your browser, it's a good time to backup it. Go to Chrome -> Settings -> Show Advanced Settings... -> SSL -> Manage certificates... You'll now see many certificates, in the searchbox type StartSSL and select all the ones that show up including the keys. Right-click and select Export items..., then save it somewhere secure where you won't lose it.

9) Now you can enter the domain name you'd like to secure. Only write the base domain name. So for https://www.google.com, you'd enter google and select .com in the dropdown menu.

10) You'll now need to verify that you are the owner of the domain name, select any of the emails that you have access to. I usually use [email protected]. If you don't have a postmaster email account, add it on your server via your CPanel or your mail server.

11) After verification, you'll need to generate the private key for your SSL certificate. You need to write down a Key Password. It's best to generate a 32-character random string. You can do that here (select 32 characters as length and unselect punctuation to generate a string with only letters and numbers). Enter this password and select 2048 again and SHA1. Store your password somewhere secure, you'll need it in a few minutes!

12) Your key is generated. Copy it and save it somewhere secure as ssl.key. Again be sure to not modify this file while copying it as you'll run into problems with NGINX later.

13) Select your domain name from the list (the only one in the list now)>

14) Now this is a bit weird, sStartSSL wants you to add at least one subdomain. Previous times, I used www or ssl as the subdomain for my SSL certificate. Even if you do not use www (like me!), you'll need to specify a subdomain for your certificate. This subdomain will show up on your certificate as its name, so don't make it too crazy. I don't believe you actually need a domain record for this subdomain to make the certificate valid but I'm not sure.

15) Now your certificate is either immediately approved or it'll be checked by StartSSL's staff. You'll receive an email later to continue the process.

16) If it's approved you can now download your ssl.crt file. Save it in the same folder you saved your ssl.key. You also need to download the sub.class1.server.ca.pem and ca.pem from StartSSL.

Install your SSL certificate on your NGINX server

17) Now upload ssl.key, ssl.crt, sub.class1.server.ca.pem and ca.pem to you server. I uploaded them to a subdirectory in my NGINX path: /etc/nginx/ssl_hostname.com/. Replace hostname.com with your domain name obviously.

18) Connect with ssh to your server and  decrypt the ssl.key with this code. You'll need to enter the 32-character password you generated before.

ssh [email protected]
openssl rsa -in /etc/nginx/ssl_hostname.com/ssl.key -out /etc/nginx/ssl_hostname.com/ssl.key

19) You'll now use the sub.class1.server.ca.pem and ca.pem certificates to 'chain' your certificate to StartSSL's. This will tell browsers how to verify your certificate (ssl.crt) with StartSSL's (the two files ending with ca.pem). We will chain the files together into one new file called ssl-unified.crt with this code in the shell:

cd /etc/nginx/ssl_hostname.com
cat ssl.crt sub.class1.server.ca.pem ca.pem > /etc/nginx/ssl_hostname.com/ssl-unified.crt

19) Now we're almost finished. We now need to change the configuration of your virtual server to accept SSL connections and use your new SSL certificate. If you use separate files for each virtual server, open the file for the server you want to accept SSL and put the code below in. Otherwise, open your main nginx.conf file and put it between the server { } braces of your server.

# ssl
ssl on;
listen 443 ssl;
ssl_certificate /etc/nginx/ssl_hostname.com/ssl-unified.crt;
ssl_certificate_key /etc/nginx/ssl_hostname.com/ssl.key;
if ($ssl_protocol = "") {
 rewrite (.*) https://$server_name$1 permanent;

We simply tell NGINX that we want SSL on, it should listen to port 443 (https://'s default port) and process it as default ssl. We then tell NGINX where to find our chained SSL certificate (ssl-unified.crt) and where our private key (ssl.key) is stored. Finally, we tell NGINX to rewrite ALL requests to the server to https.

20) Now let's try restarting NGINX. If we're lucky it works immediately.

sudo nginx -s stop
sudo nginx

or if that does not work use:

sudo service nginx restart

21) If you get the error below (like I did), you're lost. Well I was, since it's a completely unreadable error.

SSL_CTX_use_certificate_chain_file failed (SSL: error:0906D066:PEM routines:PEM_read_bio: bad end line error:140DC009:SSL routines:SSL_CTX_use_certificate_chain_file:PEM lib)

The parts that are important are "SSL_CTX_use_certificate_chain_file" and "bad end line error". Which means it has issues reading the ssl-unified.crt file. Open up the ssl-unified.crt file with the nano editor:

nano -w ssl-unified.crt

You'll probably see this:


Change it into  exactly this.


There should not be any empty lines between -----END CERTIFICATE----- and -----BEGIN CERTIFICATE-----.

Also, these lines should be EXACTLY like this. If they differ even with one dash more or less, it won't run. Trust me, I tried, for about half an hour.

Now try restarting NGINX again, and see if it works:

sudo nginx -s stop
sudo nginx

or if that does not work use:

sudo service nginx restart

If you see no errors, it works!

Test your SSL certificate

22) Now test https with curl:

curl https://hostname.com

Do you see a response? Then it works.

23) Now see if you didn't accidentally break your http:// connections:

curl http://hostname.com

Don't see a response? That was my problem too! Because we enabled port 443 for SSL before, NGINX thought we ONLY want to use that port now. So now we also enable port 80 for IPv4 and IPv6, in addition to 443. Add this to your virtual server config file:

listen   80;

Now restart NGINX.

sudo nginx -s stop
sudo nginx

or if that does not work use:

sudo service nginx restart

Does it work?

Then it means you now have successfully installed your first free SSL certificate! Have fun! You just made the world a little bittle more secure!

P.S.: You'll receive an email from StartSSL on the email you used before, to re-authenticate the certificate after 12 months. Don't forget that or your certificate will expire and your users will receive a warning when they access your site through https://.

Please let me know any comments or suggestions you have, in the thread on Hacker News or email me at hello @ this domain.

P.S. I just wrote a book on bootstrapping indie startups called MAKE. And I'm now on Twitter too if you'd like to follow more of my adventures. I don't use email so tweet me your questions.

Add HTTPS to NGINX for free and help make the world more secure

Co-Working Spaces in Chiang Mai: PunSpace

Finding an apartment in Chiang Mai

Get a tweet when I write a new post

My adventures