Setup https for local development

Kalin Chernev
3 min readJan 22, 2024

Recently I read the Best Practices for Storing Access Tokens in the Browser article and decided to experiment and create a simple BFF service with Nest, acting as a token handler, for a React app. It was harder than expected due to several key aspects, the main and very first being setting up a secure local development environment.

I’ll briefly share how I used mkcert to have proper https in both front-end and back-end. Because the browser part is easier, I’ll start with it, then move on to the server-side code. Finally, I’ll briefly mention some specifics in terms of making requests related to cookies in secure context.

mkcert

mkcert is a tool that generates locally-trusted development certificates. It works by creating a local Certificate Authority (CA) and then adding it to the trust store of your system and browsers. Certificates signed by this local CA work well in local development, whereas the quick and dirty self-signed certificates do not.

Setup https in React

React’s new docs suggest Next, Remix and Gatsby frameworks as a starting point for a new project in 2024. Although I understand the push for server components and funded products with all modern features, I still preferred going for Vite. It’s not that my app “has unusual constraints not served well by these frameworks”, it’s just that I want a working React app in a conventional way and move on.

Gladly, there’s vite-plugin-mkcert. Nothing special no mention here, that’s the whole vite.config.ts file:

The plugin setups all the necessary artifacts and starts the vite local development server in https passing the certificate to the local dev server instance. The browser serving the React app through this vite dev server trusts the certificate and can manage cookies securely. Note that the browser environment, therefore, does not care about chain of trust.

Setup https in Nest

Because Nest is a platform built on top of Node we actually work through the problem in the same way we would as it would be any other Node project.

Here’s the main.ts file:

The httpsOptions is a "small detail" part of the FAQ, whereas CORS can be enabled in two ways.

Now, let’s imagine making an internal request from the dedicated client service to a core API:

error AxiosError: unable to verify the first certificate ... error trace code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',

What’s the issue? Node uses an hardcoded list of certificate authorities. Meaning that the flow used before for the browser is not fully acceptable for the server code.

Additional CA certificates can be added using the NODE_EXTRA_CA_CERTS environment variable. For example:

Where rootCA.pem is a certificated created and signed by mkcert. This might be different from operating system to another and output destination of the certificate generation operation.

Notes on cookies

Firstly, best practices recommend that a cookie is both secure so that it's transferred only through https and it's also httpOnly so that browser scripts cannot get the contents of the cookie.

Same holds true for unsetting a cookie

Secondly, withCredentials is necessary when requests are to accept cookies in a secured environment:

The same option under fetch is credentials:

Conclusions

Although working with https and cookies securely during local development turned out to not be as easy as expected, it is worth the effort.

Originally published at https://kalinchernev.github.io.

--

--