Back

How to Enable Local HTTPS for Development

How to Enable Local HTTPS for Development

Most frontend developers assume they need HTTPS running locally to use browser APIs like service workers or geolocation. That’s actually a misconception — browsers already treat localhost as a secure context, so those APIs work fine over plain HTTP. But there are real situations where you genuinely need local HTTPS: testing OAuth callback URLs, working with cookies in a production-like environment, developing against a custom hostname, or matching production behavior precisely. This article shows you how to set it up cleanly.

Key Takeaways

  • Browsers treat localhost as a secure context, so most secure-context APIs work without HTTPS locally.
  • Local HTTPS is useful for testing production-like cookie behavior, OAuth callbacks, custom hostnames, or mobile device access.
  • Self-signed certificates cause persistent browser warnings — use mkcert to create a trusted local certificate authority instead.
  • Never commit the rootCA-key.pem file, and set NODE_EXTRA_CA_CERTS if Node.js needs to trust your local CA.

When You Actually Need Local HTTPS

Before reaching for a certificate, ask whether you actually need one. http://localhost is treated as a potentially trustworthy origin by all major browsers. Service workers, camera access, and most secure-context APIs work without any HTTPS setup. You can confirm this behavior in the browser compatibility data on webstatus.dev.

You may still want real local HTTPS when:

  • You’re testing cookie behavior in conditions that match production, especially with Secure cookies on non-localhost hostnames
  • You’re testing OAuth or OIDC flows with callback URLs that must match a registered HTTPS origin
  • You’re developing against a custom hostname like app.localhost instead of localhost
  • You need to test on a mobile device on the same network
  • Your frontend and backend both run locally and must communicate over HTTPS to replicate production behavior

If none of these apply, skip the setup and keep things simple.

Why Self-Signed Certificates Don’t Work Well

The obvious first instinct is to generate a self-signed certificate. The problem: browsers don’t trust certificates unless they’re signed by a known certificate authority. A self-signed cert triggers the “Your connection is not private” warning, and you’ll have to click through it every time — or disable certificate verification entirely, which creates bad habits.

The right approach is to create a local certificate authority that your system and browsers trust, then issue certificates signed by that CA. That’s exactly what mkcert does.

Setting Up Trusted Local Certificates with mkcert

mkcert is a zero-configuration tool that creates a local CA, registers it with your system trust store, and issues certificates signed by it. Browsers trust those certificates without warnings.

Step 1: Install mkcert

On macOS:

brew install mkcert
brew install nss  # required for Firefox

On Linux, use your package manager or follow the mkcert installation guide. On Windows, use Chocolatey or Scoop.

Step 2: Register the local CA

mkcert -install

This creates a root CA and adds it to your system’s trust store. You only need to do this once per machine.

Step 3: Generate a certificate for your hostname

mkcert localhost
# or for a custom hostname:
mkcert app.localhost

This produces two files: a certificate (.pem) and a private key (-key.pem). Keep these out of version control — add them to your .gitignore.

Configuring Your Dev Server to Use HTTPS

How you pass the certificate to your dev server depends on your tooling.

Vite — the simplest path, using vite-plugin-mkcert:

import mkcert from 'vite-plugin-mkcert'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [mkcert()]
})

The plugin handles certificate generation and server configuration automatically.

Vite (manual):

import fs from 'fs'
import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    https: {
      key: fs.readFileSync('./localhost-key.pem'),
      cert: fs.readFileSync('./localhost.pem'),
    }
  }
})

Next.js — use the next dev --experimental-https flag available in recent versions, or configure a custom Node.js HTTPS server manually using the same certificate files.

Node.js (any framework):

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

https.createServer({
  key: fs.readFileSync('localhost-key.pem'),
  cert: fs.readFileSync('localhost.pem'),
}, app).listen(3000)

Important: Never share or commit the rootCA-key.pem file that mkcert generates. If someone obtains that file, they can issue trusted certificates for any domain on your machine. Run mkcert -CAROOT to find where it’s stored.

A Security Note on Node.js

If your local backend makes HTTPS requests to other local services, Node.js won’t automatically trust your mkcert CA — it uses its own bundled list of trusted authorities rather than the system trust store. Fix this by setting an environment variable:

export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem"

Add this to your shell profile (.bashrc, .zshrc, etc.) so it persists across sessions.

Conclusion

Enabling HTTPS locally isn’t something every project needs — but when you do need it, a self-signed certificate won’t cut it. mkcert gives you properly trusted localhost certificates in minutes, without browser warnings or workarounds. Set it up once, point your dev server at the generated files, and your local environment will behave exactly like production.

FAQs

No. All major browsers treat localhost as a secure context, so service workers, the Geolocation API, and other secure-context features work over plain HTTP on localhost. You only need local HTTPS if you are testing production-like cookie behavior, OAuth callbacks against an HTTPS origin, developing with a custom hostname, or testing on a mobile device over the network.

Yes, mkcert is safe for local development. It creates a certificate authority that only your machine trusts. The key risk is the rootCA-key.pem file it generates. If someone else obtains that file, they could issue certificates your browser would trust. Never commit it to version control, and run mkcert -CAROOT to check where it is stored.

Node.js does not use your operating system's trust store. It relies on its own bundled list of certificate authorities, so it will not automatically trust your mkcert CA. Set the NODE_EXTRA_CA_CERTS environment variable to point at your mkcert rootCA.pem file, and add it to your shell profile so it persists across terminal sessions.

Yes. Any dev server or framework that accepts a TLS key and certificate file will work. Generate the files with mkcert, then pass them to your server configuration. Express, Fastify, Webpack Dev Server, and others all support custom HTTPS options. The vite-plugin-mkcert plugin simply automates this step for Vite projects.

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay