Skip to content
← All articles

Self-Signed Certificates: When to Use Them and How to Avoid Warnings

Self-Signed Certificates: When to Use Them and How to Avoid Warnings

A self-signed certificate is signed by the owner's own key, not by a trusted CA. Browsers don't trust it and show ERR_CERT_AUTHORITY_INVALID. Still, self-signed is a valid tool for internal networks, development, and test environments. This guide shows how to create one correctly, when it's appropriate, and how to free your team from constant warnings.

When self-signed is the right choice

For a public site, self-signed is always a bad idea — users see a red screen, search engines demote you. Use Let's Encrypt.

Creating a self-signed certificate with openssl

A basic 365-day cert for localhost:

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
    -days 365 -nodes \
    -subj "/C=US/ST=CA/O=Dev/CN=localhost"

Modern browsers require SAN (Subject Alternative Name) — without it you get ERR_CERT_COMMON_NAME_INVALID. Proper way — with a config:

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

[req]
C  = US
CN = localhost

[v3_req]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[alt_names]
DNS.1 = localhost
DNS.2 = dev.example.com
DNS.3 = *.dev.example.com
IP.1  = 127.0.0.1
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
    -days 365 -nodes -config openssl.cnf

Install in nginx and verify

server {
    listen 443 ssl;
    server_name localhost;
    ssl_certificate     /etc/nginx/certs/cert.pem;
    ssl_certificate_key /etc/nginx/certs/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
}
curl -k https://localhost/     # -k ignores self-signed
openssl s_client -connect localhost:443 -servername localhost

Removing the browser warning (trust store)

The warning disappears when the cert (or its CA) is added to the system trust store.

macOS:

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

Linux (Debian/Ubuntu):

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

Windows (admin PowerShell):

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

Firefox has its own trust store — add via Settings → Privacy & Security → Certificates.

The right way: local CA + mkcert

Instead of making certs by hand for every project — create a local CA, install it once, then mint certs under it. The mkcert tool does this in two commands:

# Install (macOS)
brew install mkcert
mkcert -install      # Creates and trusts the CA system-wide

# Generate a cert
mkcert localhost 127.0.0.1 dev.example.com "*.dev.example.com"
# Produces localhost+3.pem and localhost+3-key.pem

For a dev team: store the root CA in an encrypted vault (1Password / HashiCorp Vault), each dev imports once, then mkcert mints certs on demand.

Internal PKI for organizations

At mid/large-size companies, ad-hoc self-signed doesn't scale. You need a PKI:

  1. Root CA (offline, on an isolated machine) — signs only intermediates.
  2. Intermediate CA (online) — issues server certs.
  3. Tools: HashiCorp Vault (PKI secrets engine), Step CA, cert-manager for Kubernetes.
  4. Trust distribution: root CA pushed via AD group policy / MDM to all workstations.

This mirrors how public CAs work, just inside your company. Certs auto-renew via ACME (like Let's Encrypt) or Vault API документацию.

Risks and anti-patterns

Frequently asked questions

Why doesn't Let's Encrypt work for localhost?

Let's Encrypt requires public validation (DNS-01 or HTTP-01). Not possible for localhost or private IPs. Workaround — DNS-01 against a real domain like dev.example.com with DNS pointing to 127.0.0.1.

Is self-signed more secure than HTTP?

Technically yes — traffic is encrypted. But without trust you can't detect MITM; an attacker can swap the self-signed cert for theirs and you won't notice.

Can I reuse one self-signed cert for multiple domains?

Yes via SAN. Add all domains to alt_names. Wildcard works too: DNS.1 = *.example.local. See wildcard vs SAN.

What validity period should I set?

365 days is fine for dev. For internal PKI use 90 days max (forced rotation). Public CAs haven't issued more than 398 days since 2020 — good reference.

Conclusion

Self-signed is a tool, not a sin — appropriate for local dev, internal services, and IoT. Just automate via mkcert or a PKI, and never use on public sites. The enterno.io SSL Checker catches accidental self-signed deploys to prod, and Monitors tracks expiry for internal certs.

X.509 spec — RFC 5280. mkcert — github.com/FiloSottile/mkcert.

Check your website right now

Check now →
More articles: SSL
SSL
HSTS and HSTS Preload: Complete Guide to Forced HTTPS
15.04.2026 · 8 views
SSL
How to Check SSL Certificate and Never Miss Expiration
12.04.2026 · 12 views
SSL
TLS 1.3 vs TLS 1.2: What Changed and How to Migrate Correctly
15.04.2026 · 9 views
SSL
Expired SSL Certificate: How to Fix NET::ERR_CERT_DATE_INVALID
15.04.2026 · 8 views