Skip to content
Control Plane Labs

526 Invalid SSL Certificate

Server error response defined by developers.cloudflare.com.

Last updated July 27, 2026

Common causes at a glance

  • The origin certificate expired and nothing renewed it
  • A self-signed certificate on a zone set to Full (strict)
  • An incomplete chain: the leaf is served without its intermediate CA
  • The hostname is absent from the certificate's Common Name and SAN list

Reproduce it

openssl s_client -connect 203.0.113.10:443 -servername www.example.com -verify_return_error </dev/null

What 526 Invalid SSL Certificate means

The line between 526 and 525 is worth being precise about, because the fixes are unrelated. A 525 means no encrypted channel was established. A 526 means one was, and Cloudflare then rejected the certificate. The documented criteria are ordinary: not expired, not revoked, signed by a recognised CA rather than self-signed, hostname present in the Common Name or Subject Alternative Name, and a complete chain.

The mode requirement is narrower than for 525. Only Full (strict) validates origin certificates; plain Full encrypts without checking. That is why an expired origin certificate sits unnoticed for months and then takes the site down the moment somebody tightens the SSL/TLS mode.

There is no RFC

526 has no specification and no IANA registration. Cloudflare’s error documentation is the only definition. RFC 9110 §15 says an unrecognised 5xx must be handled as 500, so any monitor that does not special-case Cloudflare reports a generic server error. As with 525, the response comes from the edge. Your origin completed a handshake and then saw the connection close before a request arrived, which most web servers log as a truncated connection, if at all. Do not read a quiet access log as proof the origin is healthy.

What actually causes it

Expiry and incomplete chains account for most of these, and the incomplete chain is the crueller one because it is invisible in a browser. Browsers cache intermediates they have seen elsewhere and happily show a padlock for a server that only sends its leaf; Cloudflare’s edge rejects a chain it cannot build. The site looks fine to whoever tested it and returns 526 in production. Self-signed certificates are the deliberate version of the same problem — reasonable for origin traffic, but Full (strict) rejects them unless you load them into the Custom Origin Trust Store.

How to debug it

Validate against a real trust store rather than skipping verification, since skipping it is what hides the bug:

openssl s_client -connect 203.0.113.10:443 -servername www.example.com \
-verify_return_error </dev/null 2>&1 | grep -E 'Verify return|depth|subject='

A Verify return code: 0 (ok) means Cloudflare should accept it too. Anything else names the problem: certificate has expired, self signed certificate, or unable to get local issuer certificate, which is the incomplete-chain message. Count the depth lines too — depth 0 only is a leaf with no intermediate.

The TLS inspector shows expiry, SAN entries, and the served chain in one view. A Cloudflare Origin CA certificate is free and trusted by the edge by default. Downgrading to Full silences the error without fixing it.

Working examples

curl

# Validate properly — do not pass -k, it hides the exact failure.
openssl s_client -connect 203.0.113.10:443 \
  -servername www.example.com -verify_return_error </dev/null 2>&1 \
  | grep -E 'Verify return|depth|subject=|issuer='

# Expiry dates and SAN entries in one shot.
openssl s_client -connect 203.0.113.10:443 -servername www.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -dates -subject -ext subjectAltName

Python (requests)

import socket, ssl

# Full validation, exactly as Cloudflare's Full (strict) mode would do it.
ctx = ssl.create_default_context()
try:
    with socket.create_connection(("203.0.113.10", 443), timeout=10) as sock:
        with ctx.wrap_socket(sock, server_hostname="www.example.com") as tls:
            cert = tls.getpeercert()
            print("valid until:", cert["notAfter"])
            print("SAN:", [v for k, v in cert.get("subjectAltName", ())])
except ssl.SSLCertVerificationError as exc:
    # e.g. "certificate has expired", "unable to get local issuer certificate"
    print("validation failed ->", exc.verify_message)

Node (fetch)

import tls from "node:tls";

// Default rejectUnauthorized mirrors Full (strict). If this errors,
// Cloudflare will return 526 for the same reason.
const sock = tls.connect(
  { host: "203.0.113.10", port: 443, servername: "www.example.com" },
  () => {
    const cert = sock.getPeerCertificate(true);
    console.log("valid to:", cert.valid_to, "| SAN:", cert.subjectaltname);
    console.log("chain:", cert.issuerCertificate ? "has intermediate" : "leaf only");
    sock.end();
  },
);
sock.on("error", (e) => console.log("rejected:", e.code)); // CERT_HAS_EXPIRED, etc.

Reference and tooling

The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.

Other 5xx codes