Generate a self-signed TLS certificate with OpenSSL

Create a SAN-bearing test certificate with OpenSSL 3.x, protect the private key, and understand the separate requirement for client trust.

Best practices
Secure network connections and certificate management

OpenSSL can generate a private key and self-signed certificate in one command. Include Subject Alternative Name (SAN) entries for the exact hostnames and IP addresses clients use. Matching SAN entries solve identity coverage; clients must still trust the certificate or its issuer.

Generate a key and certificate together

This example targets OpenSSL 3.x:

umask 077
openssl req -x509 -newkey rsa:2048 -noenc -days 365 \
  -keyout server.key -out server.crt \
  -subj "/CN=example.local" \
  -addext "subjectAltName=DNS:example.local,DNS:*.example.local,IP:192.168.1.10" \
  -addext "basicConstraints=critical,CA:FALSE" \
  -addext "extendedKeyUsage=serverAuth"

Replace the sample names and IP with the endpoints you actually use. -noenc writes an unencrypted private key, suitable for a server that must start without a passphrase prompt but requiring strict file protection. -days controls the validity period. Older versions use -nodes, which is deprecated in OpenSSL 3.x.

The explicit CA:FALSE makes the certificate a leaf rather than a certificate-signing CA. Self-signed describes who signed the certificate; it does not automatically determine its CA permissions.

Generate the same components in three steps

First create a passphrase-protected RSA private key:

umask 077
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
  -aes-256-cbc -out private.key

Omit the encryption option only when your deployment deliberately manages an unencrypted key through file permissions or a suitable secret store. -nodes is a req option, not the way to disable encryption in this genpkey command.

Next create a CSR with the intended extensions:

openssl req -new -key private.key -out certificate.csr \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" \
  -addext "basicConstraints=critical,CA:FALSE" \
  -addext "extendedKeyUsage=serverAuth"

Then self-sign it, copying these locally controlled extensions:

openssl x509 -req -days 365 -in certificate.csr -signkey private.key \
  -copy_extensions copy -out certificate.crt

Review extensions before signing a CSR from another source; blindly copying arbitrary requested extensions is not an appropriate CA policy. See OpenSSL req and x509.

Inspect the result

For the one-command example:

openssl x509 -in server.crt -noout -ext subjectAltName
openssl x509 -in server.crt -noout -dates
openssl x509 -in server.crt -noout -text

Use certificate.crt instead for the three-step example. Confirm the expected names, dates, and extensions before loading the certificate into the service.

Establish trust deliberately

An ordinary public browser will not trust a newly generated self-signed certificate automatically. For local development, mkcert can create a development CA, install local trust, and issue leaf certificates. Directly trusting a self-signed leaf is another possibility where the client supports it.

For a public service, use a publicly trusted CA such as Let's Encrypt. For an internal service, distribute trust through your managed PKI rather than asking users to bypass warnings. Protect keys with restrictive permissions, such as chmod 600 server.key, and keep them out of Git.

Common questions

Why does Chrome report COMMON_NAME_INVALID?

Check the actual served certificate for a missing SAN or a hostname/IP mismatch. A trust error is a separate problem; adding SAN does not make an untrusted certificate trusted.

Can a self-signed certificate use a wildcard?

Yes. DNS:*.example.local covers one label beneath example.local; it does not cover the apex or deeper names. Add separate SAN entries as needed.

How is this different from mkcert?

mkcert creates a local CA and uses it to sign server certificates. Trusting that CA lets supported clients accept those leaf certificates. Its rootCA-key.pem is therefore highly sensitive: do not share it or use a development CA for production.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting