A self-signed certificate takes thirty seconds and one OpenSSL command to create. It costs nothing, requires no validation, and encrypts traffic with exactly the same algorithms as a certificate from a commercial CA. So why does every browser greet it with a full-page security warning?
The short answer: encryption and trust are two different things, and a self-signed certificate only provides the first. This guide explains what self-signed certificates actually are, why browsers reject them, where they're genuinely useful, how to create one properly, and what to use instead when the warnings become a problem.
What is a self-signed certificate?
Every X.509 certificate is a signed statement: "this public key belongs to this name, says the issuer." With a normal SSL/TLS certificate, the issuer is a Certificate Authority — an organization whose own root certificates are pre-installed in operating systems and browsers, and which verified that you control the domain before signing. With a self-signed certificate, the issuer and the subject are the same entity: you sign your own statement with your own private key.
Structurally, the two are nearly identical. A self-signed certificate has a subject, a public key, validity dates, and SANs, just like a CA-issued one. The giveaway is that the Issuer and Subject fields match, and the signature was made by the certificate's own key rather than a CA's. The format is defined in RFC 5280, which treats self-signed certificates as a normal, valid construct — in fact, the entire public trust system depends on them.
Every root CA certificate is self-signed
The trust anchors at the top of every certificate chain are themselves self-signed — there is no higher authority to sign them. The difference is distribution: root CA certificates earned their way into OS and browser trust stores through years of audits and root program reviews. Your self-signed certificate hasn't, and that's the entire problem.
Why browsers don't trust them
When a browser receives a certificate during the TLS handshake, it builds a chain from that certificate up to a root it already trusts. A self-signed certificate is a chain of one with an unknown issuer, so validation fails immediately. Each browser words it differently:
| Browser | What the user sees | Error code |
|---|---|---|
| Chrome / Edge | "Your connection is not private" | NET::ERR_CERT_AUTHORITY_INVALID |
| Firefox | "Warning: Potential Security Risk Ahead" | MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT |
| Safari | "This Connection Is Not Private" | (no code shown) |
On most pages the user can click "Advanced" and proceed anyway. Two important exceptions: if the domain has HSTS active, the bypass option disappears entirely and the connection hard-fails. And outside the browser, most TLS clients — curl, programming-language HTTP libraries, mobile apps, payment webhooks — verify certificates by default and simply refuse the connection with no prompt at all.
It's worth being precise about what the warning means. The browser is not saying the encryption is weak — it's saying it has no way to verify who is on the other end. An attacker performing a man-in-the-middle attack can generate their own self-signed certificate for your hostname in seconds, and to the browser it looks exactly as (un)trustworthy as yours.
Self-signed vs CA-signed: what actually differs
| Self-signed | CA-signed (publicly trusted) | |
|---|---|---|
| Encryption strength | Identical — same TLS, same ciphers | Identical — same TLS, same ciphers |
| Identity verification | None — anyone can issue one for any name | Domain control verified (plus org identity for OV/EV) |
| Browser warnings | Full-page interstitial on every new client | None — normal padlock |
| Maximum validity | Unlimited — you choose | 199 days since March 2026 (SC-081v3) |
| Revocation | None — compromised keys stay "valid" | CRL / OCSP infrastructure run by the CA |
| Cost | Free | Free (DV via ACME) to a few hundred dollars (EV) |
| Issuance accountability | None — no logs, no audit trail | Certificate Transparency logs, audited per CA/Browser Forum rules |
Notice the validity row: the CA/Browser Forum's shortening lifetimes apply only to publicly-trusted certificates. Self-signed certificates sit entirely outside that system — which is occasionally cited as an advantage for internal infrastructure, and occasionally the reason a forgotten 10-year certificate is still protecting something important with a key generated in a different decade.
The real risks
The browser warning is the visible cost. The structural risks are easier to miss:
No defense against impersonation
The entire value of a certificate is binding a key to an identity. A self-signed certificate asserts its own identity, which any attacker can do equally well. Unless every client pins the exact certificate (and most setups don't), a man-in-the-middle is undetectable.
Warning fatigue trains users badly
If your team clicks through "Your connection is not private" on the internal wiki every morning, they will click through it on a phishing page too. Normalizing certificate warnings inside an organization undermines the one signal browsers have for transport security.
No revocation, no expiry pressure, no inventory
If the private key leaks, there is no CRL or OCSP to revoke the certificate — it remains "valid" until its expiry date, which you may have set to 2035. Self-signed certificates also bypass every certificate management process tied to CA issuance, so they tend to accumulate, undocumented, across internal services.
Disabled verification spreads
The most common workaround for self-signed certificates in code is disabling TLS verification (verify=False, -k, rejectUnauthorized: false). That flag has a way of surviving the copy-paste into production code, where it silently disables certificate checking for every connection, not just the internal one.
When self-signed is the right tool
None of this makes self-signed certificates useless. They are the right choice when there is no second party who needs to be convinced of your identity:
Reasonable uses
- Local development — testing HTTPS-only features (service workers, secure cookies, HTTP/2) on
localhost - Isolated lab and test environments — networks with no external users and no path to production
- Appliance bootstrap — routers, NAS boxes, and hypervisors ship with a self-signed certificate so the admin panel works over HTTPS before a real certificate is installed
- Machine-to-machine links with certificate pinning — when both ends are configured to accept exactly one known certificate, trust is established out of band and a CA adds nothing
Never appropriate for
- Public websites — every visitor sees a security warning; most leave
- Anything handling payments or personal data — PCI DSS requires certificates from a trusted source for public-facing systems
- APIs consumed by third parties — clients with default TLS settings will refuse to connect
- Email servers used by external clients — mail apps surface certificate errors on every connection
Creating a self-signed certificate with OpenSSL
One command generates a private key and a self-signed certificate together. The -addext flag matters: Chrome has required a Subject Alternative Name since 2017 and ignores the Common Name, so a certificate without a SAN fails validation even after you trust it.
# Key + self-signed certificate, valid 365 days, with SAN openssl req -x509 -newkey rsa:2048 -nodes \ -keyout selfsigned.key \ -out selfsigned.crt \ -days 365 \ -subj "/CN=dev.internal.example" \ -addext "subjectAltName=DNS:dev.internal.example,DNS:localhost,IP:127.0.0.1" # Verify what you created — Issuer and Subject should match openssl x509 -in selfsigned.crt -noout -subject -issuer -dates \ -ext subjectAltName
-x509 is what makes this a certificate rather than a CSR; -nodes leaves the key unencrypted so the server can start without a passphrase prompt. The full option reference is in the OpenSSL req documentation, and our OpenSSL commands cheat sheet covers key generation, inspection, and format conversion in depth.
Protect the key like any other
"It's only a self-signed cert" is not a reason to commit selfsigned.key to a repository. If clients are configured to trust this certificate, whoever holds the key can impersonate the service — and there is no revocation mechanism to undo it. Set file permissions to 600 and keep it out of version control.
Locally-trusted alternatives
If the warnings are getting in your way, the answer usually isn't to click through them faster — it's one of these:
mkcert for development machines
mkcert creates a personal CA, installs its root into your OS and browser trust stores, and then issues certificates that your machine accepts with a normal padlock. No warnings, no clicked-through interstitials, and the CA only exists on your machine.
# One-time setup: create and install the local CA mkcert -install # Issue a certificate for your dev hostnames mkcert dev.local localhost 127.0.0.1 ::1
A private CA for internal infrastructure
For more than a handful of internal services, run a small internal CA (step-ca, HashiCorp Vault, or Microsoft AD CS in Windows shops) and distribute one root certificate to your machines via MDM or group policy. You get warning-free HTTPS everywhere internally, plus the issuance discipline self-signed certificates lack: an inventory, expiry dates, and revocation. Our PKI guide explains the moving parts.
A free publicly-trusted certificate
If the host has a public DNS name — even if the service itself is only used internally — a free DV certificate via ACME removes the trust problem entirely, with automated renewal handling the short lifetimes. My-SSL Free issues these with auto-renewal, wildcard support, and expiry alerts.
Moving from self-signed to a trusted certificate
Replacing a self-signed certificate is the same process as any new issuance: generate a CSR (you can reuse the existing private key or, better, generate a fresh one), submit it to a CA, complete domain validation, and install the issued certificate plus its intermediate chain. Then verify the chain with an SSL checker — a missing intermediate produces the same ERR_CERT_AUTHORITY_INVALID error you were trying to escape.
Ready to drop the warnings?
Publicly-trusted DV certificates are issued in minutes and start at a few dollars a year — less than the time spent re-accepting a self-signed certificate is worth.