Skip to main content

How to create a https server on localhost ?

To create a localhost Node.js HTTPS server, you need two things:

  • an SSL certificate
  • built-in https Node.js module

Let's start with understanding SSL certificates. There are two types of certificates: those signed by a "certificate authority", or CA, and "self-signed certificates". A Certificate Authority is a trusted source for an SSL certificate, and using a CA certificate allows your users to trust the identity of your website. In most cases, it is best to use a certificate signed by a CA in a production environment. However, for testing purposes, a self-signed certificate will do just fine. At Tekru's we use in product the CA Let's Encrypt.

Create localhost self-signed certificats

To generate a self-signed certificate, run the following in your shell:

openssl req -x509 -out localhost.crt -keyout localhost.key \
-newkey rsa:2048 -nodes -sha256 \
-subj '/CN=localhost' -extensions EXT -config <( \
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")·

When succeeded, this will be printed on your console:

Generating a 2048 bit RSA private key
........+++
.........................................................+++
writing new private key to 'localhost.key'
-----

You can also choose to use a domain with dots in it, like www.localhost, by adding it to /etc/hosts as an alias to 127.0.0.1. This subtly changes how browsers handle cookie storage.

Production

Do not use this certificat into production

Updating your Node.js code

Put with two files, localhost.crt (the certificate) and localhost.key (the private key) in the same directory as your Node.js server file, for example /assets/certs/. This is all you need for a SSL connection.

import { createServer as createSecureServer } from "https";
import { readFileSync } from "fs";
import * as constants from "constants";

function getCapCiphers() {
return [
"ECDHE-RSA-AES128-GCM-SHA256", // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
"ECDHE-RSA-AES256-GCM-SHA384", // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
"ECDHE-RSA-AES128-SHA256", // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
"ECDHE-RSA-AES256-SHA384", // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
"AES256-GCM-SHA384", // TLS_RSA_WITH_AES_256_GCM_SHA384
"AES256-SHA256", // TLS_RSA_WITH_AES_256_CBC_SHA256
"AES128-GCM-SHA256", // TLS_RSA_WITH_AES_128_GCM_SHA256
"AES128-SHA256", // TLS_RSA_WITH_AES_128_CBC_SHA256
];
}

// Handling https
let key, cert;
const productionEnvs = ["production", "master", "main"];
const isProduction = productionEnvs.includes(process.env.NODE_ENV);
if (isProduction) {
key = readFileSync(
join(process.cwd(), "assets/certs/localhost.key")
).toString();
cert = readFileSync(
join(process.cwd(), "assets/certs/localhost.crt")
).toString();
}

// Creating the server
logger.info("Production environment, serving https");
const credentials = {
key,
cert,
secureOptions: constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1,
ciphers: getCapCiphers().join(":"),
};
httpServer = createSecureServer(credentials, app);

export default httpServer;

Now that your server is set up and started, you should be able to get the file with curl

curl -kv https://localhost:8000

or in your browser, by going to https://localhost:8000 .