# Generate a self-signed certificate

To generate a self-signed certificate and its key in one command, run `openssl req -x509 -newkey rsa:2048 -nodes -keyout private.key -out certificate.crt -days 365 -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"`. The `-addext` argument is not optional in practice: browsers ignore the Common Name and match hostnames against the SAN extension only, so a self-signed certificate without SANs is rejected even after you trust it. For local development across several hostnames, creating a small local CA is usually less painful than trusting individual self-signed certificates.

Source: https://certguru.io/openssl/generate-self-signed-certificate/
Last updated: 2026-08-19
Commands verified against OpenSSL 3.5.7.

---

## The command

*Key and self-signed certificate in one step*

```bash
openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout private.key -out certificate.crt \
  -days 365 \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1"
```

| Placeholder | What to put there |
| --- | --- |
| `-x509` | Produce a self-signed certificate instead of a CSR. |
| `-newkey rsa:2048` | Generate a new key alongside it. |
| `-nodes` | Leave the key unencrypted so a dev server can start unattended. |
| `-days 365` | Validity period. |
| `-addext subjectAltName=...` | The hostnames. Without this, browsers reject the certificate. |

> **Without SANs, trusting the certificate is not enough**
>
> Browsers have matched hostnames against the SAN extension only since Chrome 58 and ignore the
>   Common Name entirely. A self-signed certificate with just a CN produces
>   `ERR_CERT_COMMON_NAME_INVALID` even after you have added it to the trust store — which sends
>   people down a long and wrong debugging path.

## Covering several hostnames

```bash
openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout private.key -out certificate.crt -days 365 \
  -subj "/CN=dev.local" \
  -addext "subjectAltName=DNS:dev.local,DNS:api.dev.local,DNS:localhost,IP:127.0.0.1,IP:::1"
```

## An EC certificate

```bash
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
  -keyout private.key -out certificate.crt -days 365 \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
```

## Verify before trusting it

```bash
openssl x509 -in certificate.crt -noout -subject -dates -ext subjectAltName
```

Expected output:

```
subject=CN = localhost
notBefore=Aug 19 12:00:00 2026 GMT
notAfter=Aug 19 12:00:00 2027 GMT
X509v3 Subject Alternative Name:
    DNS:localhost, DNS:*.localhost, IP Address:127.0.0.1
```

If the SAN section is missing, stop and regenerate — trusting it will not help.

## Trusting it locally

#### macOS

```bash
sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain certificate.crt
```

    Chrome and Safari use the system keychain. Firefox does not — see its tab.

#### Debian / Ubuntu

```bash
sudo cp certificate.crt /usr/local/share/ca-certificates/dev-local.crt
sudo update-ca-certificates
```

    The file must end in `.crt` and contain PEM, or it is silently skipped.

#### RHEL / Fedora

```bash
sudo cp certificate.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
```

#### Firefox

Firefox ships its own trust store and ignores the operating system's on most platforms. Import
    through *Settings → Privacy & Security → Certificates → View Certificates → Authorities →
    Import*, and tick "Trust this CA to identify websites".

## Consider a local CA instead

Trusting a self-signed certificate means one trust-store operation per certificate, repeated on
every machine and every time it expires. A small local CA is trusted **once**, and every certificate
it signs afterwards is accepted automatically:

1. [Create a root CA](/openssl/create-root-ca/) — one time
2. Trust that root on your machines — one time
3. [Sign a CSR with it](/openssl/sign-csr-with-ca/) — whenever you need a certificate

For anything beyond a single throwaway hostname, that is less total work.

## Inspecting what you made

*A self-signed certificate is its own issuer*

```bash
openssl x509 -in certificate.crt -noout -subject -issuer
```

Expected output:

```
subject=CN = localhost
issuer=CN = localhost
```

Identical subject and issuer is the definition of self-signed. See
[self-signed vs CA-signed](/concepts/self-signed-vs-ca-signed/).


## Frequently asked questions

### Why does my browser still reject the certificate after I trusted it?

Almost always missing subject alternative names. Trust and hostname matching are separate checks — trusting the certificate satisfies the first, but without a SAN covering the hostname the second still fails. Regenerate with `-addext "subjectAltName=DNS:..."`.

### How long should a self-signed certificate last?

For local development, 365 days is convenient. Longer is tempting and a bad habit, because a self-signed certificate cannot be revoked — if the key leaks, your only remedy is to wait for expiry or manually remove it from every trust store it reached.

### Should I use a self-signed certificate in production?

No. Every client has to be configured to trust it individually, there is no revocation, and the usual workaround is to disable verification entirely, which removes the protection TLS provides. Use a public CA, or an internal CA if the service is internal.

### What is the difference between self-signed and a private CA?

A self-signed certificate is its own issuer, so each one has to be trusted individually. A private CA is trusted once, after which every certificate it issues is accepted automatically. For more than one or two certificates, the CA is far less work.


## References

- [openssl-req(1)](https://docs.openssl.org/3.5/man1/openssl-req/) — -x509 turns a request straight into a certificate
- [RFC 5280: X.509 and CRL profile](https://www.rfc-editor.org/rfc/rfc5280) — the specification every field on a certificate comes from

## Related pages

- https://certguru.io/openssl/create-root-ca/
- https://certguru.io/openssl/sign-csr-with-ca/
- https://certguru.io/concepts/self-signed-vs-ca-signed/
- https://certguru.io/openssl/generate-csr-with-san/
- https://certguru.io/errors/err-cert-authority-invalid/

--
CertGuru — https://certguru.io
