Skip to main content

Yesterday I learned… HTTPS in development

·3 mins

While working on a project I wanted to copy a generated link programmatically to the clipboard.

I figured I have 2 ways to do that either with the wildly supported document.execCommand("copy"):

const input = document.getElementById("myInput");
input.select();
input.setSelectionRange(0, 99999);
document.execCommand("copy");

or using the async navigator api:

navigator.clipboard.writeText(text)

Since I didn’t have an input field, just a piece of data in a react component, I went with the second option.

Little I know about the fact that the navigator object only has the clipboard accessible in a trusted environment. That means HTTPS.

HTTPS in localhost? #

To test the piece of code I looked for ways to use a secured connection in a dev environment.

With React and create react app, one can start the development server with https like this, but this is not quite enough.

"scripts": {
  "start": "HTTPS=true react-scripts start",
  ...
},

This is half the story as I needed to start the backend service with https too. And somehow I had to convince Chrome that the self signed cert comes from a trusted CA.

The plan #

  • Create a locally trusted Certificate Authority (CA) that can
  • Create and sign a certificate for localhost, 127.0.0.1, ::1 domains
  • So I can use this cert in my app to achieve the S in HTTPS.

MKCERT #

Found this as an alternative to OpenSSL, and I choose to install it as a global package with yarn, on macOS. There are other options with homebrew, and for Windows.

yarn global add mkcert

Create the CA and keep the generated key safe:

mkcert create-ca

And then generate a certificate for your localhost domains:

mkcert create-cert --ca-key <pathtocakey>/ca.key --ca-cert <pathtocacert>/ca.crt --domains localhost,127.0.0.1,::1

React + Https #

To use this with React (and CRA), create a .env file with the following:

HTTPS=true
SSL_CRT_FILE=<pathToCert>/cert.crt
SSL_KEY_FILE=<pathToKey>/cert.key

Node + Https #

const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('<pathToKey>/cert.key'),
  cert: fs.readFileSync('<pathToCert>/cert.crt')
};

const server = https.createServer(options, app);

Chrome #

At the time of writing: version 89.0.xx.

After setting up and starting your app in HTTPS mode, you might come across this:

not trusted cert

Chrome doesn’t appear to accept our new shiny certificate and says it’s invalid. Click on the Certificate (invalid) to find out more:

Chrome issue

Ahha, so it says the Test CA certificate authority is not trusted. To solve this we need to add the CA to our trusted entities. On a mac open Keychain Access find the CA and mark it as trusted:

Keychain Cert Trusted details

If all went well now you should see the green lock in the browser:

valid cert

Conclusion #

I’ve learned how to use https in local development with React and Node, and since we aim to make our dev environment closer to production, this is a good way to tighten the gap and reveal issues related to https earlier.