Install
# macOS
brew install mkcert
brew install nss # if you use Firefox; mkcert needs NSS to add the cert there
# Linux
sudo apt install libnss3-tools
curl -JLO https://dl.filippo.io/mkcert/latest?for=linux/amd64
chmod +x mkcert-v*-linux-amd64
sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert
# Windows
choco install mkcert
# or: scoop bucket add extras && scoop install mkcert
# Or via mise / cargo
mise use -g mkcert@latest
mkcert -version
Install the local CA (one-time)
mkcert -install
# On Linux / macOS, prompts for sudo to install the CA into the system trust store
# On Linux, also installs into Firefox's NSS store if libnss3-tools is present
# After install, the mkcert root CA is trusted by your machine and browsers
The CA cert + private key live in $(mkcert -CAROOT) — usually ~/.local/share/mkcert/ on Linux, ~/Library/Application Support/mkcert/ on macOS.
Issue a cert for development
# Single hostname
mkcert app.local
# Multi-domain + wildcards + IPs
mkcert app.local "*.app.local" localhost 127.0.0.1 ::1
# Output: app.local.pem + app.local-key.pem (in the current directory)
Filename conventions: <first-hostname>.pem for the cert, <first-hostname>-key.pem for the private key. Pick the names you want with -cert-file + -key-file if you prefer.
Use in your dev stack
Caddy (auto-serves HTTPS)
# Caddyfile
app.local {
tls /path/to/app.local.pem /path/to/app.local-key.pem
reverse_proxy 127.0.0.1:3000
}
Vite / Node dev server
// vite.config.ts
import { defineConfig } from 'vite';
import fs from 'fs';
export default defineConfig({
server: {
https: {
cert: fs.readFileSync('certs/app.local.pem'),
key: fs.readFileSync('certs/app.local-key.pem'),
},
host: 'app.local',
port: 5173,
},
});
Python (Flask / Django dev)
flask --app=app.py run --cert=app.local.pem --key=app.local-key.pem --host=app.local
# Or for Django, use django-extensions' runserver_plus with --cert-file
python manage.py runserver_plus --cert-file=app.local.pem app.local:8000
nginx
server {
listen 443 ssl http2;
server_name app.local;
ssl_certificate /path/to/app.local.pem;
ssl_certificate_key /path/to/app.local-key.pem;
...
}
Pointing the hostname at localhost
For app.local to resolve to 127.0.0.1:
# /etc/hosts (Unix) or C:\Windows\System32\drivers\etc\hosts (Windows)
127.0.0.1 app.local api.app.local admin.app.local
Or use the .test TLD (RFC-reserved for testing; some browsers special-case it). Or use mDNS / Avahi (see that tutorial) to broadcast app.local as a real LAN name.
Share the dev cert with teammates
Each developer has their own mkcert CA — don't share private keys. Each teammate runs mkcert -install and mkcert app.local themselves; certs differ but both are valid in each developer's own browser.
For shared dev environments where multiple devs hit the same hostname, run mkcert on the dev server, distribute the CA certificate (not the CA key) to teammates' machines for trust, then use the dev-server-issued cert for the service. But for most cases, per-developer local certs are simpler.
For Docker dev environments
# Generate cert
mkcert -cert-file ./certs/app.pem -key-file ./certs/app-key.pem app.local
# Mount into the container
docker run -v $(pwd)/certs:/certs -p 443:443 my-app
# Also mount the mkcert CA so processes inside trust your local CA
docker run -v $(mkcert -CAROOT):/usr/local/share/ca-certificates/mkcert \
-v $(pwd)/certs:/certs \
my-app
For Kubernetes / kind dev
For a kind cluster needing TLS to internal services, install the mkcert CA into the cluster:
# Create a secret from the mkcert CA cert (not the key — the cluster only needs to trust, not issue)
kubectl create secret generic mkcert-ca \
-n cert-manager \
--from-file=ca.crt=$(mkcert -CAROOT)/rootCA.pem
# Use with cert-manager's CA issuer (more elaborate setup) or just bind-mount into pods that need it
For more elaborate per-cluster CA needs, graduate to step-ca (see that tutorial).
Uninstall
# Remove the CA from system + browser trust stores
mkcert -uninstall
# Then remove the CA files entirely
rm -rf $(mkcert -CAROOT)
When mkcert isn't the right tool
- For team / production CAs, mkcert is per-machine and doesn't scale. Use step-ca (see that tutorial) instead.
- For public-facing services, use Let's Encrypt — trusted by every browser without machine-side setup.
- For long-term internal services needing rotation + multi-host issuance, step-ca or HashiCorp Vault PKI.
For "make local dev HTTPS just work for me," mkcert is one command. That's the right size in 2026.