# How to Generate Certificates from CSRs with mkcert: A Complete Guide

> Easily generate certificates from CSRs with mkcert. Learn how to use the -csr flag to create locally trusted development certificates signed by the mkcert root CA.

- Repository: [Filippo Valsorda/mkcert](https://github.com/FiloSottile/mkcert)
- Tags: how-to-guide
- Published: 2026-03-05

---

**Yes, mkcert supports generating certificates from Certificate Signing Requests (CSRs) using the `-csr` flag, which creates a locally-trusted development certificate signed by the mkcert root CA.**

The `FiloSottile/mkcert` tool is widely used for creating locally-trusted development certificates, and its CSR signing capability allows you to generate certificates from private keys that never leave your system. This feature is particularly useful when you need to maintain control of your private keys while still obtaining a trusted certificate for local development.

## How the `-csr` Flag Works in mkcert

When you invoke mkcert with the `-csr` flag, the tool reads a PEM-encoded Certificate Signing Request, validates its cryptographic signature, and issues a new certificate signed by the local mkcert Certificate Authority (CA). The resulting certificate inherits the Subject and Subject Alternative Names (SANs) specified in the original CSR.

According to the source code in [`cert.go`](https://github.com/FiloSottile/mkcert/blob/main/cert.go), the `makeCertFromCSR` function handles this process by:

1. Parsing the CSR using `x509.ParseCertificateRequest`
2. Verifying the CSR signature with `CheckSignature`
3. Building a certificate template that copies the CSR's subject and DNS names
4. Signing the final certificate with the mkcert CA key

## Usage Constraints and Limitations

The CSR functionality in mkcert comes with specific constraints enforced during argument parsing in [`main.go`](https://github.com/FiloSottile/mkcert/blob/main/main.go). Understanding these limitations prevents common errors:

**Allowed combinations:**
- `-install` — to install the mkcert root CA in system trust stores
- `-cert-file` — to specify a custom output filename for the generated certificate

**Prohibited combinations:**
- `-pkcs12` — PKCS#12 bundle generation is disabled with CSR mode
- `-ecdsa` — ECDSA key generation is irrelevant when signing existing CSRs
- `-client` — Client certificate generation conflicts with CSR signing
- **Host arguments** — Positional hostname arguments are not permitted when using `-csr`

The flag must appear **before** any positional arguments, though no positional arguments are allowed when processing CSRs.

## Step-by-Step: Generating a Certificate from a CSR

### Step 1: Create the CSR

First, generate a private key and Certificate Signing Request using your preferred tool. This example uses OpenSSL to create a 2048-bit RSA key:

```bash
openssl req -new -nodes -newkey rsa:2048 \
  -keyout example.key \
  -out example.csr \
  -subj "/CN=example.test" \
  -addext "subjectAltName = DNS:example.test, DNS:*.example.test"

```

The private key (`example.key`) remains on your system, while the CSR (`example.csr`) contains the public key and requested certificate details.

### Step 2: Sign the CSR with mkcert

Use mkcert to generate the certificate from your CSR:

```bash
mkcert -csr example.csr -cert-file example.pem

```

This command reads `example.csr`, validates its signature, and outputs `example.pem` — a certificate signed by your local mkcert CA that is automatically trusted by browsers on your development machine.

### Step 3: Install the Root CA (Optional)

If you haven't already installed the mkcert root CA in your system trust store:

```bash
mkcert -install

```

This step ensures that certificates generated by mkcert, including those created from CSRs, are trusted by your operating system and browsers.

## Implementation Details

The CSR functionality is implemented across two primary source files in the `FiloSottile/mkcert` repository:

**Argument Parsing ([`main.go`](https://github.com/FiloSottile/mkcert/blob/main/main.go))**

Lines 39-44 and 64-67 in [`main.go`](https://github.com/FiloSottile/mkcert/blob/main/main.go) define the `-csr` flag and enforce the constraint that it cannot be combined with incompatible flags like `-pkcs12` or host arguments:

```go
// From main.go - flag definition
csrFlag = flag.String("csr", "", "CSR file to use for certificate generation")

```

**Certificate Generation ([`cert.go`](https://github.com/FiloSottile/mkcert/blob/main/cert.go))**

The `makeCertFromCSR` function (lines 9-46 in [`cert.go`](https://github.com/FiloSottile/mkcert/blob/main/cert.go)) handles the cryptographic operations:

1. **Parsing**: The CSR is decoded using `pem.Decode` and parsed with `x509.ParseCertificateRequest`
2. **Validation**: The signature is verified using `CheckSignature` to ensure the CSR hasn't been tampered with
3. **Template Construction**: A certificate template is built copying the Subject, DNS names, and IP addresses from the CSR
4. **Signing**: The final certificate is signed using the mkcert CA's private key via `x509.CreateCertificate`

## Summary

- **mkcert supports CSR-based certificate generation** through the `-csr` flag, creating locally-trusted certificates without exposing private keys.
- **Strict flag compatibility** applies: `-csr` works only with `-install` and `-cert-file`, and cannot combine with `-pkcs12`, `-ecdsa`, `-client`, or host arguments.
- **Implementation** resides in [`main.go`](https://github.com/FiloSottile/mkcert/blob/main/main.go) (argument validation) and [`cert.go`](https://github.com/FiloSottile/mkcert/blob/main/cert.go) (`makeCertFromCSR` function), following standard X.509 CSR parsing and signing workflows.
- **Workflow**: Create CSR with external tool → Sign with `mkcert -csr <file>` → Use resulting PEM certificate in development environment.

## Frequently Asked Questions

### Can I use mkcert to sign CSRs with my own existing CA instead of the mkcert-generated CA?

No, mkcert is designed specifically to work with its own automatically generated local CA. The `makeCertFromCSR` function in [`cert.go`](https://github.com/FiloSottile/mkcert/blob/main/cert.go) always uses the mkcert CA private key stored in the mkcert data directory. If you need to sign CSRs with a custom CA, you would need to use OpenSSL or another certificate management tool.

### Why does mkcert prevent using `-csr` with the `-pkcs12` or `-client` flags?

These restrictions exist because the `-csr` flag implies you already have a private key (embedded in or associated with the CSR), making `-pkcs12` (which bundles a new key and certificate) redundant. Similarly, `-client` generates client authentication certificates with specific Extended Key Usage attributes, which conflicts with the CSR workflow where the certificate type is determined by the CSR's own attributes. The validation logic in [`main.go`](https://github.com/FiloSottile/mkcert/blob/main/main.go) enforces these mutual exclusivity rules.

### Does mkcert verify the CSR signature before signing it?

Yes, mkcert validates the CSR signature before issuing a certificate. In [`cert.go`](https://github.com/FiloSottile/mkcert/blob/main/cert.go), the `makeCertFromCSR` function calls `CheckSignature` on the parsed CSR to verify that the request hasn't been tampered with and that the requester possesses the corresponding private key. If signature verification fails, mkcert will exit with an error rather than generating an invalid certificate.

### Can I generate a certificate from a CSR that contains Subject Alternative Names (SANs)?

Yes, mkcert fully supports CSRs containing Subject Alternative Names. The `makeCertFromCSR` function in [`cert.go`](https://github.com/FiloSottile/mkcert/blob/main/cert.go) extracts both DNS names and IP addresses from the CSR's SAN extension and includes them in the generated certificate template. This means you can create a CSR with multiple hostnames or IP addresses using OpenSSL, and mkcert will preserve all of them in the final certificate.