# mkcert User Guide

mkcert is a simple, zero-config tool for making locally-trusted development
certificates. It creates a local Certificate Authority (CA) on your machine,
adds it to the system (and optionally Firefox/Chromium and Java) trust stores,
and then issues short-lived TLS certificates for any hostname, IP address,
email, or URI you supply. The result: green padlocks in browsers, working
HTTPS in development, and no more "self-signed cert" red banners — without
asking the public web to trust you.

This guide is for developers who want to use mkcert day-to-day. For
architecture, full configuration reference, security analysis, and developer
internals, see the **mkcert Administrator & Developer Guide** in the same
output directory.

## 1. Quick Start

Install mkcert with your package manager, install the local CA into your
trust stores, and issue a certificate for `localhost` — three commands.

### Install in 60 seconds (macOS)

```bash
brew install mkcert
mkcert -install
mkcert localhost 127.0.0.1 ::1
```

That generates two files in your current directory:

```text
The certificate is at "./localhost+2.pem" and the key at "./localhost+2-key.pem"
It will expire on 28 July 2028
```

Wire those two PEM files into whatever HTTPS server you're testing — Caddy,
nginx, Express, Go's `http.ListenAndServeTLS`, Vite, Next.js dev server, etc.
Open `https://localhost` in any browser and you will see a valid, trusted
certificate. No flag-overrides, no `--insecure`, no exception clicks.

> [!TIP]
> Run `mkcert -install` only once per machine. After that, every certificate
> you mint with `mkcert <host>` is automatically trusted by the browsers and
> system services that use the OS root store, and (if installed) by Firefox
> and Java. You can re-issue per-project certificates as often as you like.

### What mkcert does (and doesn't) do

- Generates a private root CA stored in a per-user directory and adds it
  to your system, NSS (Firefox/Chromium/Chrome), and Java trust stores.
- Issues leaf certificates valid for exactly **2 years and 3 months** for
  any combination of DNS names, IP addresses, email addresses, and URIs.
- Supports wildcards (`*.example.test`), ECDSA keys, client-auth certs,
  PKCS#12 (`.p12` / `.pfx`) bundles, and certificate signing from a CSR.
- Does **not** issue publicly-trusted certificates. The CA mkcert creates
  is trusted only on your machine. mkcert certificates will not work for
  production, public websites, or anything outside your dev environment.

> [!CAUTION]
> The `rootCA-key.pem` mkcert generates is a private key for a root
> certificate authority that your machine will trust completely. Anyone who
> obtains this key can issue trusted certificates for any domain, including
> `google.com` or your bank. Never copy `rootCA-key.pem` between machines,
> commit it to source control, or share it. See Section 7 of the Admin
> Guide for the full security discussion.

## 2. Installation

mkcert is a single Go binary with no runtime dependencies. The fastest path
is your operating system's package manager. Pre-built binaries and a
build-from-source option are available for unsupported platforms.

### Choose your install method

Pick the row that matches your environment, then run one command.

| Platform | Easiest method | One-liner |
|---|---|---|
| macOS | Homebrew | `brew install mkcert` |
| macOS (MacPorts) | MacPorts | `sudo port selfupdate && sudo port install mkcert` |
| Windows | Chocolatey | `choco install mkcert` |
| Windows | Scoop | `scoop bucket add extras && scoop install mkcert` |
| Linux | Linuxbrew | `brew install mkcert` |
| Arch Linux | pacman | `sudo pacman -S mkcert` |
| Any | Pre-built binary | Download from the releases page (see below) |
| Any | Build from source | `git clone … && go build -ldflags …` |

> [!IMPORTANT]
> **macOS users:** You also need the NSS tools so mkcert can install the
> CA into Firefox. Install them with `brew install nss`. Without them,
> Firefox will not pick up your local CA and you will see "this connection
> is not secure" warnings only in Firefox.

> [!IMPORTANT]
> **Linux users:** To support Firefox and Chromium, install the certutil
> binary from your distro's NSS tools package. mkcert auto-detects the
> right command and prints the install instruction if it is missing —
> `apt install libnss3-tools` on Debian/Ubuntu, `yum install nss-tools`
> on RHEL family, or `zypper install mozilla-nss-tools` on openSUSE.

### Recommended path: package manager

#### macOS (Homebrew)

```bash
brew install mkcert
brew install nss   # required for Firefox trust
```

#### macOS (MacPorts)

```bash
sudo port selfupdate
sudo port install mkcert
sudo port install nss
```

#### Windows (Chocolatey)

```powershell
choco install mkcert
```

#### Windows (Scoop)

```powershell
scoop bucket add extras
scoop install mkcert
```

#### Linux (Homebrew on Linux)

```bash
brew install mkcert
```

If you are not on a distro with a native package, Homebrew on Linux is the
smoothest path. It installs the binary into `~/.linuxbrew/bin` (or
`/home/linuxbrew/.linuxbrew/bin` on shared machines).

#### Arch Linux

```bash
sudo pacman -Syu
sudo pacman -S mkcert
sudo pacman -S nss   # for Firefox trust
```

### Fallback: pre-built binary

If your platform isn't listed above, download a pre-built binary from the
GitHub releases page at
[github.com/FiloSottile/mkcert/releases](https://github.com/FiloSottile/mkcert/releases),
mark it executable, and put it on your `PATH`.

```bash
# Linux x86_64 example
curl -L "https://dl.filippo.io/mkcert/latest?for=linux/amd64" -o mkcert
chmod +x mkcert
sudo mv mkcert /usr/local/bin/
mkcert -version
```

> [!NOTE]
> Pre-built binaries are published for `linux/amd64`, `linux/arm`,
> `linux/arm64`, `darwin/amd64`, `darwin/arm64`, and `windows/amd64`.
> Other platforms must build from source.

### Last resort: build from source

You need Go 1.18 or newer.

```bash
git clone https://github.com/FiloSottile/mkcert
cd mkcert
go build -ldflags "-X main.Version=$(git describe --tags)"
./mkcert -version
```

The `-ldflags "-X main.Version=…"` step is what makes `mkcert -version`
print a real version string instead of `(devel)`.

> [!WARNING]
> Building inside a Go module that is not the mkcert module will leave
> `mkcert -version` reporting `(devel)`. This is a known Go limitation,
> not a bug — it is documented in the mkcert source comments. Either
> build outside any other module, or pass an explicit `-X main.Version=…`
> linker flag.

### Install the local CA

Whichever method got you the binary, the next step is the same: install
the local CA into your trust stores. Do this **once** per machine.

```bash
mkcert -install
```

Expected output:

```text
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️
The local CA is now installed in the Firefox and/or Chrome/Chromium trust store (requires browser restart)! 🦊
The local CA is now installed in Java's trust store! ☕️
```

The exact emoji-flagged lines you see depend on which trust stores are
detected on your machine. mkcert auto-detects:

- The OS system store (macOS Keychain, Windows root store, Linux
  `/etc/ssl/...` or equivalent).
- NSS-based browsers — Firefox, Chrome, and Chromium — when `certutil` is
  on your PATH.
- The Java keystore at `$JAVA_HOME/lib/security/cacerts` (or
  `$JAVA_HOME/jre/lib/security/cacerts` for older JDKs), when `keytool` is
  available.

> [!IMPORTANT]
> Restart any open browser after running `mkcert -install`. Browsers cache
> the trust store at startup; without a restart, freshly-issued
> certificates may still show as untrusted in tabs that were already open.

### Verify your install

Confirm mkcert is on your PATH and find out where it stores the CA:

```bash
mkcert -version
mkcert -CAROOT
```

Expected output:

```text
v1.4.4
/home/jane/.local/share/mkcert
```

The exact `-CAROOT` location varies by platform — see the platform table in
Section 3.

## 3. Configuration

mkcert is built to be zero-config. Everything you'd normally need to tune is
exposed as either a CLI flag or one of three environment variables. There
are **no config files** — nothing in `~/.config`, no `mkcert.yaml`, nothing
to edit. If you want to change behavior, you set an env var or pass a flag.

### Environment variables

| Variable | Purpose | Default |
|---|---|---|
| `CAROOT` | Override the CA storage directory. Useful for keeping multiple parallel CAs (one per project, one for CI, etc.). | Per-platform path (see table below). |
| `TRUST_STORES` | Comma-separated allow-list of trust stores. Valid entries: `system`, `java`, `nss`. | All detected stores. |
| `JAVA_HOME` | Required only for Java trust-store integration. Tells mkcert where to find `keytool` and `cacerts`. | Read from environment. |

The CA storage location, when `CAROOT` is unset, is computed from the OS
and a small set of standard environment variables.

| Platform | Default CAROOT |
|---|---|
| Windows | `%LocalAppData%\mkcert` |
| macOS | `$HOME/Library/Application Support/mkcert` |
| Linux | `$XDG_DATA_HOME/mkcert` (if set) or `$HOME/.local/share/mkcert` |

You can always ask mkcert directly:

```bash
mkcert -CAROOT
```

### Limit which trust stores get the CA

By default, `mkcert -install` writes the CA into every trust store it
detects. Set `TRUST_STORES` to limit that to just the ones you want. The
value is a comma-separated subset of `system`, `java`, `nss`.

```bash
# Install only into the OS system store (skip Firefox + Java)
TRUST_STORES=system mkcert -install

# Install only into Firefox/Chromium (skip system + Java)
TRUST_STORES=nss mkcert -install
```

> [!TIP]
> The `TRUST_STORES` filter applies to both `-install` and `-uninstall`.
> If you only ever installed into `nss`, set the same value when you
> uninstall — otherwise mkcert will quietly skip stores it can't find
> the CA in, which is harmless but might look confusing.

### Run multiple CAs side-by-side

Because every aspect of CA storage is keyed on `$CAROOT`, you can keep as
many independent CAs as you want by switching directories. This is the
recommended way to keep "personal" and "client work" CAs separate, or to
build a one-off CA for a CI pipeline.

```bash
# Personal CA (default)
mkcert example.test

# Client-X CA (separate root, separate trust)
CAROOT=$HOME/.mkcert-clientx mkcert -install
CAROOT=$HOME/.mkcert-clientx mkcert client-x.test
```

Each CA gets its own `rootCA.pem` and `rootCA-key.pem` inside its directory.

## 4. Core Usage

Most of mkcert's surface area is the leaf-certificate command. Run mkcert
with one or more host arguments and it issues a cert. Everything else is
trust-store management.

### Top-level commands

| Command | What it does |
|---|---|
| `mkcert -install` | Create the local CA (if missing) and install it into every detected trust store. |
| `mkcert -uninstall` | Remove the local CA from every trust store. **Does not delete the CA on disk.** |
| `mkcert <hosts…>` | Issue a leaf certificate covering the listed hosts. |
| `mkcert -CAROOT` | Print the directory where the CA is stored. |
| `mkcert -help` | Print full help, including advanced options. |
| `mkcert -version` | Print mkcert version and exit. |

### Issue a certificate

Pass any combination of DNS names, IP addresses, email addresses, and URIs
on the command line. mkcert validates each, normalizes IDN domains to
punycode, and emits a single cert covering all of them as Subject
Alternative Names (SANs).

```bash
mkcert example.com myapp.dev localhost 127.0.0.1 ::1
```

Expected output:

```text
Created a new certificate valid for the following names 📜
 - "example.com"
 - "myapp.dev"
 - "localhost"
 - "127.0.0.1"
 - "::1"

The certificate is at "./example.com+4.pem" and the key at "./example.com+4-key.pem" ✅

It will expire on 28 July 2028 🗓
```

Two files appear in the current directory:

- `example.com+4.pem` — the certificate (PEM-encoded, world-readable, mode 0644).
- `example.com+4-key.pem` — the private key (PEM-encoded, mode 0600).

The filename pattern is the first hostname plus `+N` where N is the count of
extra hosts. Wildcards have `*` replaced by `_wildcard`, and colons in IPv6
addresses are replaced by underscores.

### Choose your output filenames

If you don't want the auto-generated names, point each output to a custom
file with the corresponding flag:

```bash
mkcert -cert-file server.crt -key-file server.key example.test
```

For PKCS#12, use `-p12-file`:

```bash
mkcert -pkcs12 -p12-file bundle.p12 example.test
```

### CLI flag reference

| Flag | Type | Effect |
|---|---|---|
| `-install` | bool | Install local CA into all detected trust stores. |
| `-uninstall` | bool | Remove the CA from all detected trust stores. |
| `-CAROOT` | bool | Print the CAROOT path and exit. |
| `-help` | bool | Print short + advanced usage and exit. |
| `-version` | bool | Print the mkcert version and exit. |
| `-cert-file FILE` | string | Write the leaf certificate to FILE. |
| `-key-file FILE` | string | Write the leaf private key to FILE. |
| `-p12-file FILE` | string | Write the PKCS#12 bundle to FILE (requires `-pkcs12`). |
| `-client` | bool | Issue a client-authentication cert (sets `extKeyUsage` clientAuth). |
| `-ecdsa` | bool | Use an ECDSA P-256 key instead of RSA-2048. |
| `-pkcs12` | bool | Output a PKCS#12 (`.p12` / `.pfx`) bundle instead of PEM. |
| `-csr CSR` | string | Sign the supplied CSR file instead of generating a new key. |

> [!NOTE]
> All flags use a single dash (`-install`, not `--install`). This is Go's
> standard `flag` package style and is the only form mkcert accepts.

### Combining flags

Most flags can be combined. The hard rules are:

- `-install` and `-uninstall` cannot be set together.
- `-CAROOT` cannot be combined with `-install` or `-uninstall`.
- `-csr` is exclusive with `-pkcs12`, `-ecdsa`, and `-client`. The only
  flags allowed alongside `-csr` are `-install` and `-cert-file`.
- `-csr` cannot be combined with positional host arguments — the hosts are
  taken from the CSR itself.

If you violate any of these, mkcert exits with `ERROR: …` immediately and
issues nothing.

## 5. Common Tasks

The recipes below cover the day-to-day workflows that nearly every mkcert
user hits eventually. Each one lists the goal, what you need first, the
exact steps, how to verify, and what to do if it fails.

### 5.1 Issue a certificate for localhost

**Goal:** A trusted certificate so `https://localhost` works in your local
dev server with no browser warnings.

**Prerequisites:**

- mkcert installed.
- `mkcert -install` already run on this machine.

**Steps:**

```bash
mkcert localhost 127.0.0.1 ::1
```

**Verify:**

```bash
ls localhost+2.pem localhost+2-key.pem
openssl x509 -in localhost+2.pem -noout -text | grep -A1 "Subject Alternative Name"
```

You should see DNS:`localhost`, IP:`127.0.0.1`, IP:`::1` listed.

**If it fails:** If you see `the local CA is not installed in the system
trust store`, run `mkcert -install` and retry. If you don't see a `+2`
suffix on the filename, you only passed one host — that's fine, the file
will just be named `localhost.pem` / `localhost-key.pem`.

### 5.2 Issue a certificate for a custom dev domain

**Goal:** A trusted certificate for `myapp.test` (or any custom dev TLD)
that you point at `127.0.0.1` via `/etc/hosts` or a local DNS resolver.

**Prerequisites:**

- mkcert installed and `mkcert -install` complete.
- A line like `127.0.0.1 myapp.test` in your `hosts` file, or a wildcard
  resolver like dnsmasq pointing `*.test` at loopback.

**Steps:**

```bash
mkcert myapp.test
```

**Verify:**

```bash
curl -v https://myapp.test/   # once your dev server is running
```

The `* SSL certificate verify ok.` line confirms trust.

**If it fails:** `curl: (6) Could not resolve host` means the hosts file
or resolver isn't set up — that's a DNS problem, not a mkcert problem.
`certificate verify failed: unable to get local issuer certificate` means
`mkcert -install` was not run, or your client (curl) is using its own
bundled CA store rather than the system store. Use `--cacert
"$(mkcert -CAROOT)/rootCA.pem"` for clients that ignore the system store.

### 5.3 Issue a certificate with multiple SANs (including a wildcard)

**Goal:** Cover several services from one cert: a wildcard, the apex,
localhost, and an IPv4 loopback.

**Prerequisites:** `mkcert -install` complete.

**Steps:**

```bash
mkcert "*.dev.local" dev.local localhost 127.0.0.1
```

**Verify:**

```bash
openssl x509 -in _wildcard.dev.local+3.pem -noout -text | grep "DNS:\|IP:"
```

You should see all four names.

**If it fails:** Quote the wildcard. If you see `no matches found`, that's
your shell expanding `*` — wrap it in single or double quotes
(`"*.dev.local"`).

> [!WARNING]
> X.509 wildcards only match one DNS label. `*.dev.local` covers
> `app.dev.local` but not `api.staging.dev.local`. mkcert prints a
> reminder when you issue a wildcard cert. Many browsers also reject
> second-level wildcards like `*.local` — mkcert prints an explicit
> warning when it sees one.

### 5.4 Issue a PKCS#12 bundle for legacy applications

**Goal:** A `.p12` (or `.pfx`) bundle for IIS, older Java apps, or any
tool that wants the cert and key combined into a single PKCS#12 file.

**Prerequisites:** `mkcert -install` complete.

**Steps:**

```bash
mkcert -pkcs12 example.test
```

**Verify:**

```bash
openssl pkcs12 -in example.test.p12 -info -nokeys -password pass:changeit
```

**If it fails:** PKCS#12 bundles are encrypted with the password
**`changeit`** — this is a deliberate, hardcoded default for compatibility
with the broad set of tools that expect that password. mkcert prints this
in the output of every PKCS#12 issue. If your tool prompts for a password,
that's the one.

> [!IMPORTANT]
> The PKCS#12 password is `changeit` — always. It's hardcoded in the
> source and is intentional: many tools assume that default password. If
> you need a different password, re-encrypt with `openssl pkcs12 -in
> bundle.p12 -out new.p12 -nodes` then re-export with the password you
> want.

### 5.5 Issue a client-authentication certificate

**Goal:** A cert with `extKeyUsage = clientAuth`, suitable for mTLS or
client-cert auth in a browser/curl.

**Prerequisites:** `mkcert -install` complete.

**Steps:**

```bash
mkcert -client [email removed]
```

**Verify:**

```bash
openssl x509 -in 'jane@example.test-client.pem' -noout -text | grep -A1 "Extended Key Usage"
```

You should see both `TLS Web Client Authentication` and (because the SAN
is an email) `E-mail Protection`.

**If it fails:** mkcert validates each argument as DNS, IP, URI, or email
in turn. If your value isn't recognized as any of those, you get
`ERROR: "<value>" is not a valid hostname, IP, URL or email`. Quote email
addresses to keep your shell from interpreting `@`.

### 5.6 Sign a custom CSR

**Goal:** Issue a certificate from a CSR generated elsewhere (your CI,
your load balancer, your hardware token).

**Prerequisites:**

- A CSR file in PEM format with type `CERTIFICATE REQUEST` or `NEW
  CERTIFICATE REQUEST`.
- `mkcert -install` complete.

**Steps:**

```bash
mkcert -csr request.csr
```

**Verify:** mkcert prints the output filename and SANs:

```text
Created a new certificate valid for the following names 📜
 - "myapp.test"

The certificate is at "./myapp.test.pem" ✅
```

**If it fails:**

- `failed to read the CSR: unexpected content` — the file isn't a PEM
  block. Re-export.
- `expected CERTIFICATE REQUEST, got …` — the PEM type is wrong (often
  `CERTIFICATE` instead of `CERTIFICATE REQUEST`). Generate a CSR, not a
  cert.
- `invalid CSR signature` — the CSR was tampered with or its signature
  doesn't match the embedded public key. Regenerate.

> [!NOTE]
> When using `-csr`, mkcert ignores `-pkcs12`, `-ecdsa`, and `-client`,
> and you cannot pass extra host arguments — names come from the CSR's
> `subjectAltName` extension (or, as a fallback, the CSR's Common Name).

### 5.7 Find and back up your CA

**Goal:** Locate the CA on disk so you can back it up, or copy it to
another machine you control.

**Prerequisites:** mkcert installed.

**Steps:**

```bash
mkcert -CAROOT
ls "$(mkcert -CAROOT)"
```

**Verify:**

```text
/home/jane/.local/share/mkcert
rootCA-key.pem  rootCA.pem
```

If you want to back the CA up, copy the entire directory to a trusted
backup location:

```bash
tar -czf mkcert-ca-backup.tar.gz -C "$(mkcert -CAROOT)" .
```

**If it fails:** If `-CAROOT` prints nothing, mkcert couldn't compute a
default. That happens on Linux when both `XDG_DATA_HOME` and `HOME` are
empty. Set `CAROOT` explicitly: `export CAROOT=/path/of/your/choice`.

> [!CAUTION]
> `rootCA-key.pem` is the private key for a root CA your machine
> trusts unconditionally. Treat the backup file at least as carefully as
> you treat your SSH private keys. Encrypt the archive at rest, and never
> commit it.

### 5.8 Uninstall the local CA

**Goal:** Remove the CA from your trust stores when you're done with
mkcert (or moving to a different machine).

**Prerequisites:** None.

**Steps:**

```bash
mkcert -uninstall
```

Expected output:

```text
The local CA is now uninstalled from the system trust store(s)! 👋
```

**Verify:** Re-run `mkcert <some host>`. mkcert will print
`Note: the local CA is not installed in the system trust store` along
with whichever stores it could not find the CA in.

**If it fails:** `mkcert -uninstall` only removes the CA from trust
stores. The CA files (`rootCA.pem`, `rootCA-key.pem`) **stay on disk** at
`-CAROOT`. To purge them too:

```bash
rm -rf "$(mkcert -CAROOT)"
```

Do this only if you are certain you don't want any previously-issued
certificates to be re-installable.

### 5.9 Use an ECDSA key instead of RSA

**Goal:** Smaller, faster keys (P-256 ECDSA) for tools or servers where
that matters.

**Prerequisites:** `mkcert -install` complete.

**Steps:**

```bash
mkcert -ecdsa example.test
```

**Verify:**

```bash
openssl x509 -in example.test.pem -noout -text | grep "Public Key Algorithm"
```

You should see `Public Key Algorithm: id-ecPublicKey`.

**If it fails:** ECDSA leaf certs work in every modern browser. If your
specific tool rejects them, drop `-ecdsa` and use the default RSA-2048.
The CA itself uses RSA-3072 regardless of `-ecdsa` — that flag only
affects the leaf key.

## 6. Troubleshooting

When something goes wrong, the first move is almost always to re-run the
failing command and read the actual error. mkcert prints clear `ERROR:`,
`Warning:`, and `Note:` lines and exits non-zero on hard failures.

### Diagnostic decision tree

1. **Is `mkcert` on your PATH?** `mkcert -version` should print a version.
2. **Is the CA installed?** `mkcert -install` should be idempotent — run
   it. If it prints "already installed", you're good.
3. **Did you restart the browser?** Browsers cache trust at startup.
4. **Is the certificate actually loaded by your server?** A trusted cert
   in a file is useless if your server is using a different one.

### Error: `failed to find the default CA location, set one as the CAROOT env var`

**Cause:** mkcert couldn't compute a default `CAROOT` because both
`XDG_DATA_HOME` and `HOME` are unset (typically only happens in stripped
container environments or some CI runners).

**Solution:**

```bash
export CAROOT=$HOME/.mkcert
mkcert -install
```

**Prevention:** Set `CAROOT` explicitly in CI configs and ephemeral
containers — never rely on the auto-detected default in those
environments.

### Error: `can't create new certificates because the CA key (rootCA-key.pem) is missing`

**Cause:** mkcert is running in "keyless mode" — it found `rootCA.pem`
but not `rootCA-key.pem` in `CAROOT`. This happens when you copy only the
public CA cert to a machine and try to issue from it.

**Solution:** Either issue certs on the machine that has the key, or copy
`rootCA-key.pem` to the keyless machine. Keyless mode supports `-install`
but not certificate issuance.

**Prevention:** When provisioning a new dev machine, copy both
`rootCA.pem` AND `rootCA-key.pem` from the source `CAROOT`.

### Error: `"<value>" is not a valid hostname, IP, URL or email`

**Cause:** mkcert validates every positional argument as DNS, IP, URL, or
email and rejects anything that doesn't match. Common causes:

- A trailing comma or space inside what you thought was one hostname.
- A hostname with characters mkcert's regex doesn't allow (only
  `0-9a-z._-` and an optional leading `*.`).
- A URL that's missing a scheme (`example.com/path` — no scheme means
  it's parsed as a hostname, and `/path` makes it invalid).

**Solution:** Quote each host, drop trailing punctuation, and lowercase
everything. For URIs, include the scheme: `spiffe://trust-domain/foo`.

**Prevention:** Echo your command back before running it — trailing
characters are easy to miss.

### Error: `no Firefox and/or Chrome/Chromium security databases found`

**Cause:** mkcert searches a fixed list of NSS database paths
(`~/.pki/nssdb`, `~/snap/chromium/.../nssdb`, `/etc/pki/nssdb`,
plus Firefox profile directories). If none exist, you get this error.
This is the situation tracked by issue
[#120](https://github.com/FiloSottile/mkcert/issues/120).

**Solution:**

1. Open Firefox or Chromium at least once. NSS databases are created
   lazily on first run.
2. Confirm the database exists: `ls ~/.pki/nssdb` (Chromium) or check
   your Firefox profile dir.
3. Re-run `mkcert -install`.

**Prevention:** On a fresh user account, start the browsers once before
running `mkcert -install`.

### Error: Firefox on macOS still shows "Not Secure"

**Cause:** Several open issues track this — [#326](https://github.com/FiloSottile/mkcert/issues/326),
[#370](https://github.com/FiloSottile/mkcert/issues/370), and
[#662](https://github.com/FiloSottile/mkcert/issues/662). The common root
causes are: (a) the NSS tools aren't installed (`brew install nss` was
skipped); (b) Firefox is using a custom profile directory that mkcert
doesn't search (issue
[#521](https://github.com/FiloSottile/mkcert/issues/521)); (c) Firefox
Developer Edition uses a separate trust DB.

**Solution:**

1. `brew install nss` and re-run `mkcert -install`.
2. Restart Firefox completely (not just the window — quit the app).
3. If you use a custom profile, copy `rootCA.pem` and import it
   manually via Firefox → Settings → Privacy & Security → View
   Certificates → Authorities → Import.

**Prevention:** Stick to the default Firefox profile if you want
mkcert to manage trust automatically.

### Error: `SSL_ERROR_RX_RECORD_TOO_LONG` in Firefox

**Cause:** Tracked in issue [#361](https://github.com/FiloSottile/mkcert/issues/361).
This is almost always a server-side misconfiguration: your dev server
is speaking plain HTTP on the port Firefox is connecting to as HTTPS.
mkcert is not at fault — it's a wiring problem.

**Solution:** Confirm your dev server is actually listening for TLS on
the port you're hitting. For Vite, that's `server.https = true`; for
Express, `https.createServer({key, cert}, app)`; for nginx,
`ssl on; ssl_certificate …; ssl_certificate_key …;`.

**Prevention:** When wiring up a new dev server, hit it once with
`curl -v https://localhost:PORT` first — `curl` errors are clearer than
browser ones for TLS-vs-HTTP confusion.

### Error: `"sudo" is not available, and mkcert is not running as root`

**Cause:** On Linux/macOS, mkcert needs root to update the system trust
store. It uses `sudo` by default; if `sudo` isn't on your PATH and you
aren't already root, the install of the system store will be skipped or
will fail.

**Solution:** Either run mkcert as root (`su -; mkcert -install`) or
install `sudo` (`apt install sudo`, etc.).

**Prevention:** Don't run mkcert in stripped containers without sudo —
use `TRUST_STORES=nss mkcert -install` if you only need browser trust.

### Error: PKCS#12 won't import — wrong password

**Cause:** The PKCS#12 export password is hardcoded to **`changeit`**.
If your tool is prompting and `changeit` doesn't work, the file is
corrupted, not the password.

**Solution:** Re-issue the bundle: `mkcert -pkcs12 -p12-file new.p12
example.test`. Use the `changeit` password to import.

**Prevention:** Save the bundle straight to where it's needed; don't
roundtrip through scp/email/etc., which can corrupt binary files if a
client treats them as text.

### Error: Cert is rejected by Traefik / cert-manager / your tool

**Cause:** Tracked in issue
[#646](https://github.com/FiloSottile/mkcert/issues/646) for Traefik,
[#350](https://github.com/FiloSottile/mkcert/issues/350) for cert-manager
comparisons. mkcert certs are short-lived (≈2y3m) and are signed by a CA
your tool may not trust by default. Tools running inside containers do
not see your host's trust store.

**Solution:** Mount the CA into the container at the right path:

```bash
docker run -v "$(mkcert -CAROOT)/rootCA.pem:/etc/ssl/certs/mkcert-rootCA.pem" …
```

or copy it into the container image during build.

**Prevention:** When containerizing your dev stack, treat the mkcert
root CA as a build-time secret/asset and mount it explicitly.

### Note: macOS 26.1 — root certificate no longer applicable for leaf certs

**Cause:** Tracked in issue
[#651](https://github.com/FiloSottile/mkcert/issues/651). Newer macOS
releases tightened root-cert acceptance rules.

**Solution:** Watch the upstream issue. As of commit `1c1dc4e`, no
mitigation has shipped in mkcert itself. Workaround: re-issue with a
shorter validity if your tool requires it, or pin the root through your
app rather than the OS keychain.

**Prevention:** None — track the issue.

## 7. FAQ

**Q: Where does mkcert store the CA?**

In a per-user directory determined by your OS. On macOS it's
`$HOME/Library/Application Support/mkcert`; on Windows, `%LocalAppData%\mkcert`;
on Linux, `$XDG_DATA_HOME/mkcert` or `$HOME/.local/share/mkcert`. Override
this with the `CAROOT` environment variable. To see the active path:

```bash
mkcert -CAROOT
```

**Q: How long are mkcert certificates valid for?**

Leaf certificates are valid for **2 years and 3 months**. The CA itself
is valid for **10 years**. macOS and iOS reject leaf certs valid for
longer than 825 days, so mkcert's 2y3m is the safe ceiling.

**Q: Can I use mkcert in production?**

No. The CA mkcert creates is trusted only on machines where you've run
`mkcert -install`. Anyone else hitting your service will see a "not
trusted" error. For production use Let's Encrypt or a commercial CA. See
the Admin Guide Section 7 for the full security discussion.

**Q: Why doesn't `mkcert -version` print a real version when I built from source?**

Go embeds the version of the main module via `debug.BuildInfo`, but if you
build mkcert as part of a different module (or directly from a Git clone
without any module context), Go reports `(devel)`. Build with
`go build -ldflags "-X main.Version=$(git describe --tags)"` to bake in a
real version. This is documented in mkcert's source comments referencing
[golang/go#29814](https://github.com/golang/go/issues/29814) and
[golang/go#29228](https://github.com/golang/go/issues/29228).

**Q: How do I share my CA between machines I own?**

Copy the entire `CAROOT` directory (both `rootCA.pem` and `rootCA-key.pem`)
to the same path on the other machine, then run `mkcert -install` there.
Treat the copy operation like sharing an SSH private key — encrypted
transport, no clouds you don't control. See Section 5.7.

**Q: Can I customize the root CA's name (Organization, Common Name, etc.)?**

Not via flags. The CA's `Subject` is hardcoded to `O=mkcert development CA,
OU=<user>@<host>, CN=mkcert <user>@<host>`. Issue
[#229](https://github.com/FiloSottile/mkcert/issues/229) tracks this as a
feature request. If you must customize, build mkcert from source with a
patched `newCA()` in `cert.go`.

**Q: Does mkcert support IPv6?**

Yes. Pass IPv6 addresses on the command line just like IPv4:

```bash
mkcert localhost ::1 fe80::1
```

The output filename uses underscores in place of colons.

**Q: Why is the PKCS#12 password `changeit`?**

It's the de facto standard default for keystores (Java's cacerts uses it,
many tools assume it). mkcert hardcodes it for compatibility with the
broadest set of tools. There is no flag to change it; re-encrypt with
`openssl` if you need a different password.

**Q: Can I limit which trust stores mkcert touches?**

Yes — set `TRUST_STORES` to a comma-separated subset of `system`, `nss`,
`java`. Example: `TRUST_STORES=system mkcert -install` only writes to the
OS root store.

**Q: How do I issue a cert with a wildcard?**

Quote it so your shell doesn't expand the asterisk:

```bash
mkcert "*.example.test"
```

The output filename will be `_wildcard.example.test.pem`. Wildcards only
match one DNS label (the asterisk does not match dots), and many browsers
reject second-level wildcards like `*.test` — mkcert prints a warning
when it sees one. See Section 5.3.

**Q: Can I use mkcert without installing the CA into the system store?**

Yes — skip `mkcert -install`, and use clients that take an explicit CA
file. With `curl --cacert "$(mkcert -CAROOT)/rootCA.pem"` you can verify
mkcert-issued certs without touching any trust store. This is also useful
in CI: ship `rootCA.pem` as a build artifact, point your test clients at
it.

**Q: Does mkcert work on Apple Silicon / ARM Linux?**

Yes — pre-built binaries exist for `darwin/arm64` and `linux/arm64`. See
the GitHub releases page. For other ARM variants (e.g. armv5), build from
source with the appropriate `GOARCH`. Issue
[#652](https://github.com/FiloSottile/mkcert/issues/652) discusses
cross-arch support more broadly.

**Q: How do I rotate or regenerate the CA?**

Run `mkcert -uninstall`, then `rm -rf "$(mkcert -CAROOT)"`, then
`mkcert -install`. mkcert will create a fresh CA. **Every previously
issued leaf certificate becomes invalid** the moment you do this — they
were signed by a CA that no longer exists in any trust store. Re-issue
the certs you still need.

**Q: Is mkcert affected by recent macOS / iOS root-cert restrictions?**

Some are tracked in upstream issues — see issue
[#651](https://github.com/FiloSottile/mkcert/issues/651) for the macOS
26.1 changes and [#343](https://github.com/FiloSottile/mkcert/issues/343)
for general expiration discussion. mkcert already targets the 825-day
ceiling Apple platforms enforce, so most users are unaffected.

---

_Generated April 2026 from FiloSottile/mkcert at 1c1dc4e. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
