News
🛡️ Security Tutorials Generate a Self-Signed SSL Certificate with openssl

Generate a Self-Signed SSL Certificate with openssl

Create a certificate and private key for local development, internal tools, and test environments where a CA-signed certificate is not necessary.

A self-signed certificate provides the same encryption as one issued by a certificate authority — the difference is that browsers and clients do not trust it automatically and show a warning. For local development and internal services where you control the client, that trade-off is fine.


Generate a certificate and key in one command

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
  -sha256 -days 365 -nodes \
  -subj "/CN=localhost"

What this produces:

  • key.pem — the private key
  • cert.pem — the self-signed certificate

What the flags do:

  • -x509 — output a self-signed certificate instead of a certificate signing request
  • -newkey rsa:4096 — generate a new RSA key with a 4096-bit key size
  • -sha256 — sign the certificate with SHA-256
  • -days 365 — certificate valid for one year
  • -nodes — do not encrypt the private key (no passphrase). Necessary when the key is used by a server process that cannot prompt for input.
  • -subj "/CN=localhost" — set the Common Name without the interactive prompt

Add Subject Alternative Names

Modern browsers and TLS clients require the hostname to appear in the Subject Alternative Names (SAN) extension, not just the Common Name. A certificate without SANs is rejected by Chrome, Safari, and many libraries even if the CN matches.

Create a config file:

# san.cnf
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = myapp.internal

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = myapp.internal
DNS.2 = localhost
IP.1  = 127.0.0.1
IP.2  = 192.168.1.50

Generate the certificate using the config:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
  -sha256 -days 365 -nodes -config san.cnf

Now the certificate covers both the hostname and IP addresses in the alt_names block.


Inspect a certificate

Verify what you generated:

openssl x509 -in cert.pem -text -noout

The output shows the validity period, the subject, and — most importantly — the Subject Alternative Names section. If X509v3 Subject Alternative Name is missing or does not list your hostname, the certificate will be rejected by modern clients.

Check the expiry date specifically:

openssl x509 -in cert.pem -noout -dates

Trust the certificate locally

Self-signed certificates produce browser warnings because they are not in the system's certificate trust store. Add them to stop the warnings on your own machine.

macOS:

sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain cert.pem

Ubuntu / Debian:

sudo cp cert.pem /usr/local/share/ca-certificates/myapp.crt
sudo update-ca-certificates

Windows (PowerShell):

Import-Certificate -FilePath cert.pem -CertStoreLocation Cert:\LocalMachine\Root

Restart your browser after adding the certificate. Chrome caches trust state aggressively — a full restart (not just a new tab) is required.


Use the certificate with a web server

Nginx:

server {
    listen 443 ssl;
    server_name myapp.internal;

    ssl_certificate     /etc/ssl/myapp/cert.pem;
    ssl_certificate_key /etc/ssl/myapp/key.pem;
}

Node.js (https module):

const https = require('https');
const fs = require('fs');

const server = https.createServer({
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.pem'),
}, app);

server.listen(443);

Protect the private key

The private key must stay private. If someone obtains it, they can impersonate your server for the certificate's validity period.

chmod 600 key.pem
chown root:root key.pem

Do not commit key.pem to version control. Add it to .gitignore:

key.pem
*.pem

For production services reachable from the internet, use a certificate from Let's Encrypt instead of a self-signed one. certbot automates issuance and renewal, the certificates are free, and clients trust them without any manual configuration.