Skip to main content

    Self-Signed Certificates: What They Are, Risks, and When to Use One

    What a self-signed certificate is, how it differs from a CA-signed certificate, the browser warnings it triggers, and when self-signing is the right choice.

    MS
    My-SSL Security Team
    ·
    13 min read
    ·Published June 9, 2026

    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:

    BrowserWhat the user seesError 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-signedCA-signed (publicly trusted)
    Encryption strengthIdentical — same TLS, same ciphersIdentical — same TLS, same ciphers
    Identity verificationNone — anyone can issue one for any nameDomain control verified (plus org identity for OV/EV)
    Browser warningsFull-page interstitial on every new clientNone — normal padlock
    Maximum validityUnlimited — you choose199 days since March 2026 (SC-081v3)
    RevocationNone — compromised keys stay "valid"CRL / OCSP infrastructure run by the CA
    CostFreeFree (DV via ACME) to a few hundred dollars (EV)
    Issuance accountabilityNone — no logs, no audit trailCertificate 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.

    bash
    # 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.

    bash
    # 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.

    Frequently asked questions