How to check certificate revocation

Ask openssl to fetch the CRL named in the certificate and apply it: openssl s_client -connect host:443 -servername host -crl_download -crl_check </dev/null. A revoked certificate returns Verify return code: 23 (certificate revoked). The plain handshake without those two flags reports 0 (ok) for the same host.

Why check this

Revocation is the control you rely on after a key leaks, so it belongs in the incident drill and in the sign-off for any certificate rotation. The failure it prevents is silent: the certificate you retired stays usable, because most clients never ask whether it is still valid. Run the check on the old certificate after a rotation, not on the new one.

Prerequisites

Steps

  1. Step 1.

    Run the ordinary handshake first, so the trap is visible.

    openssl s_client -connect revoked.badssl.com:443 -servername revoked.badssl.com </dev/null 2>&1 | grep 'Verify return code'
    
        Verify return code: 0 (ok)

    The certificate on this host was revoked by its CA. openssl still reports 0 (ok), because revocation is off unless you ask for it.

  2. Step 2.

    Turn revocation on and repeat.

    openssl s_client -connect revoked.badssl.com:443 -servername revoked.badssl.com -crl_download -crl_check </dev/null 2>&1 | grep -E 'verify error|Verify return code'
    
    verify error:num=23:certificate revoked
      Verify return code: 23 (certificate revoked)
  3. Step 3.

    Read the serial number and the CRL address out of the certificate, for the manual route.

    openssl s_client -connect revoked.badssl.com:443 -servername revoked.badssl.com </dev/null 2>/dev/null | openssl x509 -noout -serial -ext crlDistributionPoints
    
    serial=05C391E061DE6588F670B613F061AEF4B3A1
    X509v3 CRL Distribution Points: 
      Full Name:
        URI:http://ye1.c.lencr.org/34.crl
  4. Step 4.

    Download that CRL and search it for the serial.

    curl -s -o le.crl http://ye1.c.lencr.org/34.crl && openssl crl -inform DER -in le.crl -noout -text | grep -A1 05C391E061DE6588F670B613F061AEF4B3A1
    
        Serial Number: 05C391E061DE6588F670B613F061AEF4B3A1
          Revocation Date: Jul 14 21:01:28 2026 GMT

    The revocation date is the evidence an incident report needs. -crl_download in step 2 does the same lookup and hides the date.

  5. Step 5.

    Check whether the CA offers OCSP at all before planning an OCSP query.

    openssl s_client -connect revoked.badssl.com:443 -servername revoked.badssl.com </dev/null 2>/dev/null | openssl x509 -noout -text | grep -A2 'Authority Information Access'
    
                Authority Information Access: 
                  CA Issuers - URI:http://ye1.i.lencr.org/
              X509v3 Subject Alternative Name: 

    The block lists CA Issuers and stops. There is no OCSP - URI line, so this certificate names no responder. Several large CAs removed that URI during 2025, which leaves the CRL as the only client-side route for the certificates they issue.

  6. Step 6.

    Confirm how your own HTTP client treats the same host, because the answer differs per TLS backend.

    curl -sS -o /dev/null -w 'http=%{http_code}\n' https://revoked.badssl.com/
    
    http=200

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | Verify return code: 23 | The CA lists this serial as revoked | Take the host out of rotation. The certificate is dead to any client that checks | | Verify return code: 0 (ok) without -crl_check | Nothing was checked | Rerun with -crl_download -crl_check before reporting a pass | | Verify return code: 3 (unable to get certificate CRL) | No CRL reached openssl | Add -crl_download, or fetch the URL from step 3 by hand and read the status code | | The serial appears in the CRL with a date | Confirmed revocation, with the timestamp | Paste the serial and the revocation date into the incident ticket | | The AIA block holds no OCSP - URI line | This CA publishes no OCSP responder | Use the CRL. An OCSP query has no address to reach |

Common mistakes

Sign: A revoked certificate loads over HTTPS and the tester marks revocation as working.Cause: Revocation is enforced by the client, not by the server. On this machine curl 8.21.0 built against LibreSSL returns http=200 for revoked.badssl.com, while curl 8.1.2 built against Schannel refuses the same URL with exit code 35 and CRYPT_E_REVOKED. Same certificate, same command, opposite verdicts.
Sign: Chrome shows no warning, so the certificate is assumed live.Cause: Chrome does not fetch a CRL or send an OCSP request for ordinary certificates. It ships an aggregated list, described in the Chromium [CRLSets documentation](https://www.chromium.org/Home/chromium-security/crlsets/), which covers a fraction of revocations. A browser that stays quiet proves nothing about revocation status.
Sign: Piping the CRL straight into openssl fails with Unable to load CRL.Cause: A CRL is DER, and a Git Bash pipe mangles binary on stdin. Write it to a file with curl -o first, as step 4 does, and pass -inform DER -in.
Sign: The CRL check passes for a certificate you revoked ten minutes ago.Cause: A CRL is a published file with its own schedule. The one in step 4 carries Next Update nine days after Last Update, so a fresh revocation may not appear for days. Revocation is not an instant kill switch.

What to check next

FAQ

How to check certificate revocation status from the command line?

Step 2 is the short answer: -crl_download -crl_check on openssl s_client. It fetches the CRL named in the certificate, applies it and prints code 23 when the serial is listed. Steps 3 and 4 show the same result with the revocation date attached.

How to check a certificate revocation list by hand?

Read the CRL Distribution Points extension, download the file, then parse it with openssl crl -inform DER -noout -text. Search for your serial number in uppercase hexadecimal. A hit prints the entry and the revocation date underneath it.

Why does openssl say ok for a revoked certificate?

Because s_client performs path and date validation only. Revocation costs a network round trip to the CA, so it is opt in. -crl_check on its own is not enough either: without -crl_download it returns code 3, unable to get certificate CRL.

Is OCSP better than a CRL for this check?

It was, and it is going away. Certificates from several large CAs no longer carry an OCSP URI, which step 5 shows on revoked.badssl.com. Where a responder still exists, an OCSP answer is fresher than a weekly CRL.

Does revoking a certificate stop traffic immediately?

No. Clients that check at all cache the CRL or the OCSP answer until its next update, and many clients do not check. Rotate the certificate and drop the old key from the server rather than relying on revocation alone.

Verified

Verified by Maks Vernyopenssl 3.1.1curl 8.21.0

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

intermediate8 minpublished updated Maks Verny