How to add SSL to your website using certbot and LetsEncrypt

Image for post
Image for post

Running your web server without SSL can create the impression that your content is not secure. Chrome shows a nasty “Not Secure” note next to your domain. It sucks.

But it only takes 30 minutes of your time to start serving via HTTPs on a Node/Express server. Just follow the instructions in this SSL tutorial.

Just follow me on Twitter to get updates on my free coding tutorials.

SSL Connections via HTTPS Protocol

SSL encrypts outgoing and incoming data between client and server. This helps provide increased security for data such as credit card numbers, emails and passwords. With HTTP protocol, the data is sent as-is. (Perhaps, it may have been compressed, but not really encrypted by an encryption algorithm.)

This is important because unless you implement SSL the data sent to the server is not secure. Also Chrome and other browsers will display “Not Secure” message next to your domain name which might prevent users from buying your products.

Luckily for us Node already has a module called https:

// Import the https module
let https = require("https");
// Choose port based on whether we're on
// loaclhost or production server

const port = process.env.node_env === 'production' ? 443 : 3000;
// Link to generated certificate files
// (replace example.com with your own domain name)
// (see how to generate them later in this tutorial)

const key = `/etc/letsencrypt/live/example.com/privkey.pem`;
const cert = `/etc/letsencrypt/live/example.com/fullchain.pem`;
const options = {
key: fs.readFileSync(key),
cert: fs.readFileSync(cert)
};
https.createServer(options, function(request, response) {

/* Your SSL server is running */

/* Of course here... you would write your API implementation */


}).listen(port);

But if you are running on Express, you don’t even have to do that. Express will simply take an array of cert files pointing to the certificates we will generate later in this tutorial. Here is an Express.js example:

// Import packages
const express = require('express');
const https = require('https');
const port = 443;
// Create Express app
const app = express();
let site = 'example.com';
let port = 443;
// Link to generated certificate files
// (replace example.com with your own domain name)
// (see how to generate them later in this tutorial)
const
key = `/etc/letsencrypt/live/${site}/privkey.pem`;
const cert = `/etc/letsencrypt/live/${site}/fullchain.pem`;
const certificates = {
"key": fs.readFileSync(key),
"cert": fs.readFileSync(cert)
};
const server = event => {
console.log(`${site} is listening on port ${port}!`);
};
// Launch Node server with Express "app"
https.createServer(certificates, app).listen(port, server);

Remember that every time you add a new require statement to your app you need to also actually install the package associated with it:

npm install https — save

The — save directive adds package to your package.json file.

We can now use this https module to start our server. But that’s not enough. The most important part is setting up SSL certificate so that the server does a handshake with certificate authority before serving content and a lock icon appears:

Image for post
Image for post

Lock icon on infinite sunset — my secure PWA app.

We’ll use LetsEncrypt — it’s free and easy to set up. Unlike openssl LetsEncrypt generates production-quality SSL certificates that should be enough for everything.

Let’s Encrypt

Let’s go over setting up free SSL certificates on Linux-based operating systems. The preferred OS for most web host providers. However, the commands described in this section are the same in Terminal and bash.exe on Windows.

To use LetsEncrypt we must update packages, install git (if you haven’t already,) clone and install letsencrypt repository and execute a few bash commands.

For no reason in particular I’ll use bash.exe on Windows 10 but you can use Terminal on OSX. First, I will launch bash.exe from Start menu. The command line window opens:

Image for post
Image for post

Login to your web host as root user using ssh command. Just replace xx.xxx.xx.xxx with static IP address where you host your website.

Image for post
Image for post

You will be asked to enter your root user password (unless you have passwordless login set up but that’s outside of this tutorial’s scope.)

Image for post
Image for post

We should now be logged into the web host. See server log below:

Image for post
Image for post

You will be greeted by a screen similar to this. (I’m running Ubuntu 18.)

To install certbot we need to update the packages first. This is because certbot developers continue applying improvements and your Ubuntu server may not have the latest version on initial server installation.

Run sudo add-apt-repository ppa:certbot/certbot command to add certbot repository to your Ubuntu server:

Image for post
Image for post

Just press Enter and latest packages will be added to mirror files which is a list of links pointing to the latest version of the packages.

Now run sudo apt-get update to actually download the updated packages:

Image for post
Image for post

This step is important - it will update your certbot packages to latest version.

If you find yourself on CentOS or Debian you can do the same thing as follows:

On CentOS run sudo yum update && sudo yum upgrade

On Debian run sudo apt update && sudo apt upgrade

Linux && symbols will cause your packages to be updated followed by being upgraded without having to execute each command separately.

Installing certbot

Run apt-get install certbot to install certbot package. We’re already logged in as root so there is no need to use sudo command (otherwise also prepend sudo.)

Image for post
Image for post

Press Y and hit Enter. Or run the same command with -y or — yes flag Installation log rolls on the screen and after that we should be good to go!

Installing git

To install letsencrypt we need to clone latest version of git. But in order to do that we first need to make sure we have git installed on our system:

Image for post
Image for post

The installation process will scroll on your screen…

Image for post
Image for post

We can now use git. If you’re in Terminal chances are you already have the packaget installers apt or apt-get. Keep in mind in this example we’re in bash.exe on Windows 10. If sudo or apt-get are not working there is a work-around.

First…if you are hosting your server remotely use ssh in bash.exe to log into your server and all linux commands will become available over there.

Second if you are developing on localhost and don’t need to log into your remote host, you must install Ubuntu for Windows in addition to bash.exe. Once installed you should have apt-get and other common Linux commands in your Windows 10 bash.

Cloning letsencrypt

Now we’re ready to clone the latest version of letsencrypt to our server. This is accomplished by running the following Linux command:

git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

Just copy and paste it into your Terminal or bash!

Image for post
Image for post

You might also want to precede it with sudo as shown in the following example.

Image for post
Image for post

Create SSL Certificate

Finally, we’re ready to create the SSL certificate that will ultimately enable us to serve files via https instead of http. Following previous steps type this command to navigate to newly created letsencrypt directory:

cd /opt/letsencrypt

You will navigate to the folder where textbf{letsencrypt} was just installed.

cd stands for Change Directory on Linux-based OS.

Image for post
Image for post

After that run the following command…

sudo -H ./letsencrypt-auto certonly — standalone -d A.com -d www.A.com

(Just don’t forget to replace A.com with your domain name.)

Image for post
Image for post

Initiate creation of the SSL certificate for example.com (Again, make sure it matches your domain name instead of example.)

After this you will see letsencrypt-auto command generating the needed certificate files with certbot and automatically creating http challenges.

At this stage you might be asked several questions. I’m not going to include them here to save book space. Just enter your email address when asked and few other things. Choose (A)gree or (Y)es option every time asked (required.)

Let’s take a look at what actually happened once the certificate is generated.

The important parts are highlighted:

Image for post
Image for post

LetsEncrypt created and verified http challenges (this is needed in order to verify that the domain name belongs to you, but in this instance this is done for us automatically.) It also generated two pem keys for our example.com domain.

Congratulations on your secure SSL domain!

At this point your domain name is https-enabled. Assuming you passed the two keys to your Node or Express as shown in the very beginning of this tutorial. Just restart the server and you should see the secure lock in the address bar.

But there is one more thing! Let’s check where the files live and get familiar with the directory where the keys were actually generated:

Image for post
Image for post

The certificate files were created in /etc/letsencrypt/live directory.

Output contents of live dir. This is where letsencrypt saved all certificate keys for all domain names on the server under respective folder names.

Image for post
Image for post

Run ls command to list contents of the directory. You will see that our site example.com now has a folder (should show your domain name.)

In example.com directory you will find several pem files generated by letsencrypt. We only need cert.pem and privkey.pem. We’re ready to start using the certificate. All we need to do is add some new code to the existing index.js file.

ACME Challenge

If you completed steps to install the SSL certificate in previous section you don’t need to do this next step. But in some cases ACME challenges are required by some server configurations in order to verify that you are the owner of the domain name.

Run certbot certonly — manual or certbot certonly — manual (note this is actually double dash as shown below, not one dash.)

Image for post
Image for post

If you’re hosting multiple domains, you can use the same certificate, so just specify as many domain names as you need separated by space or comma. In this case we’re simply adding example site awesomesite.com

Enter your domain name (without www.) and press Enter.

Image for post
Image for post

Type Y and press Enter to agree and proceed.

Image for post
Image for post

To verify we own the domain name, we need to manually create the file named afBX9EXvQqUqzFooe02–22EfJ5TzcniFw7CvqlaMXCA — just the part before the dot (.) highlighted in yellow in screenshot above.

Place this file into your .well-known/acme-challenge directory on your server. (At the root folder of your site.) You might have to create these folders first.

Now edit the contents of that file by pasting the whole string generated by certbot into it. In our case it is the long text afBX9EXvQqUqzFooe02–22EfJ5TzcniFw7CvqlaMXCA.rX9ThTxJ4y47aLEr7xgEWcOm4v7Jr5kJeT4PLA98–0

Keep in mind that this filename will be generated every time you run certbot. So if you’ve already ran this command before it should be changed again.

Press Enter to Continue.

Adding PEM Files To Enable HTTPS Server

Navigate to /etc/letsencrypt/live/site.com directory to verify the pem files were actually generated by steps taken in previous section.

Now that we have privkey.pem and cert.pem generated we need to pass them to our Node server configuration using an options object.

const express = require('express');
const https = require('https');
const port = 443;
// Create Express app
const app = express();
let site = 'example.com';
let port = 443;
const certificates = {
"key": fs.readFileSync(`/etc/letsencrypt/live/${site}/privkey.pem`),
"cert": fs.readFileSync(`/etc/letsencrypt/live/${site}/fullchain.pem`)
};
const server = event => {
console.log(`${site} is listening on port ${port}!`);
};
// Launch Node server with Express "app"
https.createServer(certificates, app).listen(port, server);

Just replace example.com with your actual domain name.

For https connections it is proper to use port 443. However this is not a requirement — any allowed port number will still work. Whatever port you are using you also need to open it on your system for it to start working.

Update the index.js file with code above. Log into your web host. Navigate to the root directory of your application and run node index. If all goes well at this point your server will be accessible via https protocol and the “Not Secure” message in Chrome (and some other browsers) should disappear.

But There Is One More Thing…

A Common Stumbling Point

A certificate chain is the list of certificates that contains SSL certificate, intermediate certificate authorities and root certificate authority that enables connecting device to verify SSL certificate is trustworthy.

This is required for production servers.

At this point your https site will open in Chrome and IE without a hitch! But if you open it in Firefox you may still see the lock icon. Firefox (among many other programs) hinge on checking certificate chain.

To properly set up a certificate you have to set up certificate chain not just one key. Chrome and IE gracefully overlook this detail and show connections as secure.

If you don’t link to certificate chain, you will not be able to successfully validate your https connection. This might interfere with things like adding Twitter cards to your site (because Twitter cards with images stored at a https address require chain verification) when looking up your card image via Twitter meta tags.

And that’s just one example. Many issues can arise if you don’t link to certificate chain. Luckily for us, the solution is simple. In previous steps Let’s Encrypt already generated fullchain.pem file in the same directory with cert.pem.

All we have to do change cert.pem to fullchain.pem in the previous source code example as follows.

Change the following line:

const cert = /etc/letsencrypt/live/site.com/cert.pem;

To:

const cert = /etc/letsencrypt/live/site.com/fullchain.pem;

Restart your Node server with node index.js and you should have a properly installed and fully working SSL certificate!

You can follow me on Twitter to get updates on my free coding tutorials, or just check out this page with my coding books on JavaScript and CSS if you need a copy.

Written by

Issues. Every webdev has them. Published author of CSS Visual Dictionary https://amzn.to/2JMWQP3 few others…

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store