Node.js backends that are run in development environments are usually served via HTTP by default. However, you may need your backend to be served via HTTPS if you are integrating services, using external libraries, or testing strong content security policies, among other scenarios. It’s usually vital that these situations are tested in a development environment before being deployed to production. This article will explain an array of ways to configure Secure Sockets Layer (SSL) HTTPS on a local development Node.js Express backend.
Goals:
This article aims to educate developers, particularly beginners and intermediate developers, on ways to configure SSL HTTPS on a local development Node.js Express backend.
Overview:
We will configure SSL HTTPS with the aid of a self-signed certificate on our local machine. Note that this procedure should be done only in a local/development environment! In a production environment, it’s best to obtain a certificate from a third-party Certificate Authority like letsEncrypt, Certbot, and so on. To obtain more information about a Certificate Authority, check out this explainer.
Generate SSL HTTPS using OpenSSL
OpenSSL is a cross-platform, open-source tool used for most cryptographic and network communication operations. However, OpenSSL is available by default only in most Linux distributions and macOS systems. Hence, Windows users will have to directly install OpenSSL in order to use it. You can learn how to install OpenSSL on Windows with these step-by-step instructions.
Create a Certificate Key
SSL certificates are typically signed by Certificate Authorities, which are trusted, third-party organizations. Before issuing any certificate, they conduct a thorough check on a website or web app in order to ascertain its owner, function, and use.
However, no Certificate Authority issues a certificate for localhost because no one owns it. So, we’ll simulate the signing process that Certificate Authorities use on our local machine.
Let’s create a certificate key
by typing the following commands in sequence:
$ mkdir ssl-cert
$ cd ssl-cert
$ mkdir Cert-Auth
$ cd Cert-Auth
$ openssl genrsa -out Cert-Auth.key -des3 2048
The commands above will generate a private key and ask for a simple passphrase. The user will enter and re-enter the passphrase for confirmation.
Generate a Certificate Authority (.pem) file
Upon creating a certificate key
, we’ll create a certificate authority pem file
. We need this file to create the certificate(.crt)
file.
To create the certificate(.crt)
file, type the command below:
$ openssl req -x509 -sha256 -new -nodes -days 365 -key Cert-Auth.key -out Cert-Auth.pem
In the command above, we used the already created certificate key
to generate the certificate pem file
.
Generate a Certificate Signing Request(.csr) file
The Certificate Authority (CA) key and CA certificate have now been generated. Since we've already created a CA, we can sign SSL certificates.
Next, we create a new directory called localhost in the root (ssl-cert
) directory. Create a new file, localhost.ext
, inside localhost.
$ mkdir localhost
$ cd localhost
$ touch localhost.ext
After creating the localhost.ext
file, we will open the file and add the code snippets below to it.
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
The code above simply sets the configuration that our SSL certificate will contain. Our certificate works for localhost and also 127.0.0.1. More domains can be added to this file if you want the certificate to work for other host addresses. To dive into this further, read this article.
After editing the localhost.ext file, we will generate the certificate signing request (CSR). The command below will be used to create a key, which will then be used to generate a CSR.
$ openssl genrsa -out localhost.key -des3 2048
The commands above will generate a private key and ask for a simple passphrase. The user will enter and re-enter the passphrase for confirmation.
Once you’ve done that, type the below command to generate the CSR:
$ openssl req -new -key localhost.key -out localhost.csr
The commands above will generate a CSR and ask for certain pieces of information. They will also request a challenge password. A "challenge password" is essentially an embedded, one-time shared secret between you and the SSL certificate issuer (CA) that the issuer may use to authenticate you should that ever be necessary.
Note the requested challenge password can be anything, but it should be a “strong” password.
Generate a Certificate(.crt) File
We can now use our CSR to ask the CA to sign a certificate. It should be noted that the location from which the user executes commands affects the paths for the CA.key and CA.pem files (relative paths).
$ openssl x509 -req -in localhost.csr -CA ../CA.pem -CAkey ../CA.key -CAcreateserial -days 365 -sha256 -extfile localhost.ext -out localhost.crt
This command accepts the certificate extensions file, the CA certificate (Cert-Auth.pem
and Cert-Auth.key
), and the CSR (localhost.csr
) (localhost.ext
). These parameters produce a localhost.crt certificate file that is valid for one year.
We will decrypt localhost.key
since our localhost.key
is in encrypted form. we will do this with the command below:
$ openssl rsa -in localhost.key -out localhost.decrypted.key
Develop a server using Node.js that is being served up utilizing a localhost SSL certificate
In order to test the certificate, we will create a Node.js application with express installed. To do this, we will type the commands below in sequence:
$ mkdir ssl-nodejs
$ cd ssl-nodejs
$ npm init -y
$ npm i express
$ touch index.js
Upon installing the Node.js application, we will create an Express app with a “get” API route that simply outputs “hello world” upon a visit to the route.
const express = require('express');
const app = express();
app.use(express.json())
app.get('/', (req, res)=> {
res.send('hello world')
})
module.exports = app
We will also import the Express app to our index.js file
and use it to create the server.
const http = require('http');
const fs = require("fs")
const { getDesktopFolder } = require('platform-folders')
const path = require("path")
const https = require('https')
const app = require('./app')
const server = https.createServer({
key: fs.readFileSync(`${getDesktopFolder()}/ssl-cert/localhost/localhost.decrypted.key`),
cert: fs.readFileSync(`${getDesktopFolder()}/ssl-cert/localhost/localhost.crt`)
}, app)
server.listen(3000, ()=> {
console.log('port running on', 3000)
})
Also, we added the dependency platform-folders
which simply locates the absolute path of the desktop folder. Accompanied by the filesystem module, we will use this package to locate the localhost.decrypted.key
and localhost.crt files
on our PC from an absolute path.
After creating the server, start it using the below command:
$ node index.js
Configure the Chrome web browser and the Postman API client to allow certificates we signed as the Certificate Authority (CA)
Now that our server is serving up the SSL, we can test our https://localhost:3000 link in our Chrome browser as shown below:
Even after serving up our localhost with SSL and inspecting the signed certificate, Chrome does not trust the CA that signed this certificate. This is correct behavior, because we signed it locally and didn’t use a third-party trusted Certificate Authority. Since this is for development purposes, we want to access localhost with our self-signed certificate.
Therefore, we will access the chrome://flags/#allow-insecure-localhost URL on our Chrome browser and toggle the Allow invalid certificates for resources loaded from localhost
button to enabled
.
N/B: This should not be done in production. However, we do this for development purposes in order to view the response on localhost from the browser.
Access localhost with HTTPS from the browser and the Postman API client
From Postman, access SSL certificate verification
in the general settings and make sure that SSL certificate verification is turned off.
N/B: This is not advised to be done on production. However, we do this for development purposes in order to view the response of localhost on Postman.
Once this is done, we can access the HTTPS localhost on Postman.
Generate SSL HTTPS using MK-CERT
mkcert
is a simple tool used for making locally trusted development certificates. It requires no configuration, as opposed to the OpenSSL method explored previously, which is an advantage.
Installing MK-CERT
macOs:
We’ll use Homebrew to install mk-cert
on macOS.
brew install mkcert
See more installation options for macOS.
Linux
Windows
Generate an SSL for localhost using MK-CERT
To create an SSL certificate for localhost, run the command below:
mkcert localhost
However, you can also chose to create a locally trusted CA by running the command:
mkcert -install
This creates a local CA which is only trusted by your device. To locate the path to the local CA, you can type the command below:
mkcert -CAROOT
Develop a server using Node.js that is being served up utilizing a localhost SSL certificate
We’ll use our previous server example; however, we’ll make a few tweaks to the key
and cert
values passed into the server. mk-cert
generates localhost.pem
and localhost-key.pem
files for localhost, therefore, the parameters of key and cert on our server will now contain the file paths to the localhost.pem and localhost-key.pem files, respectively.
const http = require('http');
const fs = require("fs")
const { getDesktopFolder } = require('platform-folders')
const path = require("path")
const https = require('https')
const app = require('./app')
const server = https.createServer({
key: fs.readFileSync(`${getDesktopFolder()}/mk-cert-ssl/localhost-key.pem`),
cert: fs.readFileSync(`${getDesktopFolder()}/mk-cert-ssl/localhost.pem`)
}, app)
server.listen(3000, ()=> {
console.log('port running on', 3000)
})
Configure the Chrome web browser and the Postman API client to allow certificates we have signed as the CA
Since we have already configured the web browser to view self-signed certificates, we can easily view the result below:
Access localhost with HTTPS from the browser and the Postman API client
We can access the HTTPS localhost on Postman as shown below:
Conclusion
In this article, we explored different ways of configuring SSL HTTPS on a local development Node.js Express backend and explained the terminologies used in certificate creation. To learn more about the certificate creation process, check out this in-depth article.