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.
The command #
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" Replace these values
| 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. |
Covering several hostnames #
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 #
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 #
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 #
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.
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.
sudo cp certificate.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract 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:
- Create a root CA — one time
- Trust that root on your machines — one time
- Sign a CSR with it — whenever you need a certificate
For anything beyond a single throwaway hostname, that is less total work.
Inspecting what you made #
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.
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) — -x509 turns a request straight into a certificate
- RFC 5280: X.509 and CRL profile — the specification every field on a certificate comes from