OCSP, OCSP stapling, and the revocation-check fingerprint
A certificate has an expiry date printed inside it, and for most certificates that date is the only end the certificate will ever see. But sometimes a key leaks, or a domain changes hands, or a CA discovers it mis-issued, and the certificate needs to die before its printed expiry. Revocation is the machinery for that early death. The question it has to answer is simple to state and miserable to implement: when a browser is shown a valid-looking certificate, how does it find out whether that certificate has been pulled since it was issued?
For two decades the web’s main answer to that question was OCSP, the Online Certificate Status Protocol. A browser would take the certificate it was handed, build a small query, and ask the issuing CA’s responder, live, mid-handshake, “is this one still good?” That design had a fatal privacy property baked into it, it never worked well enough to fail closed, and in 2025 the largest CA in the world turned its OCSP responders off for good. This post is about how revocation actually works on the wire: the request and response formats, the stapling extension that tried to fix the privacy leak, must-staple, and the slow collapse that ended with CRLs and browser-pushed filters winning.
The sections run in order. First CRLs, the original mechanism and the one that outlived its replacement. Then OCSP: the request, the response, the four fields that identify a certificate. Then stapling, where the server fetches the response and hands it to the client inside the handshake, and must-staple, the certificate extension that tries to make stapling mandatory. Then the privacy leak that doomed plain OCSP, with the Apple outage of 2020 as the concrete case. Then the 2023-2025 unwind: the CA/Browser Forum vote, Let’s Encrypt’s shutdown, and what CRLite and CRLSets do instead. A closing section on what a defender can still read off the revocation layer in 2026.
CRLs: the list that came first and stayed
The oldest answer is a list. A Certificate Revocation List is a signed file the CA publishes that names every certificate it has revoked but which has not yet expired. Each entry is a serial number plus a revocation date and an optional reason code. The whole file is signed by the CA, carries a thisUpdate and nextUpdate timestamp, and lives at a URL named in the certificate’s CRL Distribution Points extension. A client that wants to check a certificate downloads the relevant CRL, verifies the signature, and looks for the certificate’s serial number in the list.
The appeal is that it is simple and the client learns nothing the CA can use to track it. The CA serves one static file to everyone; it cannot tell which certificate inside that file you cared about, because you downloaded all of them. CRLs are also cacheable and replayable in the benign sense, a client can hold one until nextUpdate without phoning home again.
The problem is size and freshness, and the two pull against each other. A large CA revokes a lot of certificates, so its CRL grows large, and a browser that wanted to check revocation properly would have to download a multi-megabyte file for every CA it encountered. To keep the files small you publish often and expire entries quickly, but publishing often means clients re-download often, and a stale cached CRL is a window during which a revoked certificate still looks live. By the late 2000s the consensus was that whole-CRL download was too heavy for browsers to do inline, and the major browsers quietly stopped doing it for the general case. CRLs did not disappear. They became infrastructure that something else consumed, which turns out to be the form in which they won.
OCSP: a question about one certificate
OCSP, specified in RFC 6960, was the answer to CRL bloat. Instead of downloading the whole list, the client asks about the single certificate in front of it. The protocol is a request-response over HTTP, usually a GET with the request base64-encoded into the path, or a POST with the request as the body.
The request is small and its core is the CertID, the structure that names the certificate being asked about. A CertID has exactly four fields, and they are worth knowing because they explain both how OCSP identifies a certificate and what the responder can see. There is a hashAlgorithm, then issuerNameHash (the hash of the issuer’s distinguished name), issuerKeyHash (the hash of the issuer’s public key), and serialNumber. Notice what is not there: the certificate itself, the domain, the subject. The responder is told which CA issued the certificate (via the two issuer hashes) and which serial number to look up. It is not handed the hostname. That detail matters later, because the serial number plus the responder you contacted is often enough to reconstruct the hostname anyway.
The response is signed by the CA (or by a responder certificate the CA delegated to) and carries, for each CertID asked about, a CertStatus. There are three possible statuses. good is a positive answer, the certificate is not revoked. revoked carries a revocation time and optional reason. unknown means the responder has no record of the certificate, which is its own kind of problem because a good-only responder cannot tell you that a forged serial is fake. Alongside the status come three timestamps that govern caching and freshness: producedAt (when the responder signed this response), thisUpdate (the most recent time the status is known correct), and nextUpdate (when newer information will be available). A client may cache and reuse the response until nextUpdate. There is also an optional nonce extension, id-pkix-ocsp-nonce, an opaque octet string that binds a specific request to a specific response so an attacker cannot replay an old good answer. In practice high-volume responders disable the nonce so they can pre-sign and cache responses, which reopens exactly the replay window the nonce was meant to close.
The latency cost of that extra round trip was never small. Adam Langley, then at Google, measured the median successful OCSP check at roughly 300 milliseconds and the mean at nearly a second, a tax paid on the critical path of every fresh HTTPS connection. That cost is part of why browsers softened the check until it barely existed.
Soft-fail: a seat-belt that snaps when you crash
Here is the design flaw that hollowed out OCSP. What should a browser do when the revocation check fails to complete? Not when it returns revoked, but when the responder times out, or returns garbage, or the network drops the request. If the browser refuses the connection, it is said to hard-fail. If it shrugs and proceeds, it soft-fails.
Almost everyone soft-fails, and the reason is operational. CA responders went down often enough that hard-failing would have meant taking large parts of the web offline whenever a responder hiccuped. So browsers proceeded on failure. But soft-fail destroys the security of the check, because an attacker who can present a revoked certificate is, by assumption, a network attacker, and a network attacker can simply block the OCSP request too. The check that was supposed to catch them is the very check they can switch off. Langley’s line for this is the one everyone quotes: soft-fail revocation checks are like a seat-belt that snaps when you crash, worthless precisely because it only holds when you do not need it.
Chrome drew the conclusion in 2012 and disabled online OCSP and CRL checks by default starting in version 19. In their place Chrome ships CRLSets, a curated list of revocations Google compiles and pushes to the browser through its update channel. CRLSets are deliberately not comprehensive. Langley described them as primarily a way to block certificates quickly in emergencies, the high-value revocations like a compromised intermediate, not every routine revocation a CA issues. The argument for the push model over the live check is subtle and good: to defeat a pushed list an attacker must block the browser’s updates continuously from the moment of revocation onward, which is far harder than blocking one OCSP request during a single intercepted handshake.
Stapling: move the question to the server
OCSP stapling is the fix for both the latency and the privacy leak, and it is mostly a matter of who asks the question. Instead of the client contacting the CA, the server contacts the CA on its own schedule, gets back a signed OCSP response about its own certificate, caches it, and then hands that response to every connecting client inside the TLS handshake. The response is “stapled” to the certificate. The client gets the revocation status without ever talking to the CA.
On the wire this rides the Certificate Status Request extension from RFC 6066, extension code point 5 in the IANA registry, the one named status_request. The client puts status_request in its ClientHello to say it will accept a stapled response. The server, if it has one, echoes status_request in its half of the handshake and sends the OCSP response in a CertificateStatus message. The rule is strict in both directions: a server must not send CertificateStatus unless the client asked with status_request (or its multi-certificate successor status_request_v2 from RFC 6961), and the client only parses the stapled response when it offered the extension. The signed response the server staples is the same CertStatus structure a client would have fetched itself, with the same thisUpdate/nextUpdate window governing how long the server may keep reusing it.
Stapling is the right shape, and it still mostly failed to save OCSP, for two reasons that are worth separating. The first is reliability of the server’s own fetch. Pre-fetching a fresh OCSP response and keeping it valid is fiddly, and CA responders misbehave in ways that make it harder. Cloudflare, when it built centralized OCSP pre-fetching, catalogued the failure modes it had to absorb: responders with very short validity windows that left gaps, responders that signed with the wrong algorithm, malformed responses, load-balanced responder pools where one backend was stuck in a failed state, and a lag between a certificate being issued and its OCSP status becoming available. With centralized pre-fetching and retry queues Cloudflare got more than 99.9 percent of connections a valid staple, and saved browsers that would otherwise fetch themselves up to 30 percent of handshake time. That is the level of engineering it took to make stapling reliable, and most origin servers never came close.
The second reason is that stapling does not, by itself, close the soft-fail hole. If a browser is willing to proceed when no staple is present, then an attacker presenting a revoked certificate simply omits the staple, and the browser soft-fails exactly as before. Stapling fixes privacy and latency. It does not make revocation enforceable. For that you need a way for the certificate to insist that a staple be present.
Must-staple: making the staple mandatory
That insistence is must-staple, defined in RFC 7633 as the TLS Feature certificate extension. The mechanism is clean. A certificate carries an extension, identified by the OID id-pe-tlsfeature ({ id-pe 24 }), whose value is a list of TLS feature integers the server promises to provide. When that list contains the status_request value (5), the certificate is declaring that any legitimate server presenting it will staple an OCSP response. A client that knows this can now hard-fail safely: if the certificate says a staple is mandatory and no valid staple arrives, the connection is rejected. The RFC states the logic directly, that a client cannot draw any conclusion from the absence of in-band status unless it knows the legitimate server would have provided it. Must-staple is how the client comes to know that.
On paper this closes the loop. The certificate itself, signed at issuance and impossible for a network attacker to alter, becomes the thing that forbids the soft-fail. An attacker holding a revoked must-staple certificate cannot present it without a fresh staple, and cannot produce a fresh staple, because the responder would say revoked.
In practice must-staple died of its own brittleness. The extension is baked in at issuance and cannot be added or removed later, so committing to it is committing your certificate’s whole validity period to flawless stapling. Any stapling failure on a must-staple certificate is not a soft degradation; it is your site going dark for every visitor at once. Given how hard reliable stapling turned out to be, that trade was unattractive, and adoption stayed tiny. Browser support was lopsided too: Firefox implemented must-staple enforcement, while Chrome and Safari never shipped it. A certificate feature that one major browser honors and two ignore cannot deliver the guarantee it promises. Let’s Encrypt, which had offered must-staple, reported that only a very small fraction of subscribers used it, and made ending that fraction’s support an early step in its OCSP shutdown.
If you want the adjacent story of why a server’s exact handshake bytes become an identity all their own, the TLS 1.3 handshake frame by frame walks the message sequence that CertificateStatus slots into.
The privacy leak, made concrete
Return to that orange leg in the first diagram, the client asking the CA directly. Every plain OCSP check is a request to the CA that reveals the requester’s IP address and, through the certificate serial number, which site the requester is about to visit. The traffic was classically plain HTTP, so it was not only the CA that could see it; anyone on the path with a traffic analyzer could read off which certificates a given IP was checking, and a certificate serial maps to a hostname through public Certificate Transparency logs. Let’s Encrypt named this as the first reason for shutting OCSP down: when someone visits a site whose software checks revocation via OCSP, the CA operating the responder immediately learns which site is being visited from that visitor’s particular IP. That is a per-visit record of who browsed where, held by a third party that has no reason to want it.
The sharpest demonstration was not a browser at all. On 12 November 2020, Apple’s OCSP responder for Developer ID certificates, ocsp.apple.com, slowed under load as macOS Big Sur launched. macOS checks the signing certificate of every app you run through the trustd daemon, and trustd did not soft-fail cleanly; with the responder reachable but crawling, app launches across the world hung waiting for an answer. The outage was an availability story on the day, but it surfaced a privacy story underneath: macOS was sending a request to Apple every time a user opened an application, over unencrypted HTTP, which meant Apple, and anyone watching the network, had a real-time feed of which apps were being opened on which machines and when. Apple’s response over the following year moved the check from plaintext ocsp.apple.com to an encrypted ocsp2.apple.com endpoint. The opt-out preference it promised users at the time did not arrive in the releases that followed.
That is the structural problem stapling was built to solve and must-staple was built to enforce, and since neither got the universal deployment it needed, the leak persisted for everyone still doing plain client-side OCSP. The cleanest way to stop leaking is to stop asking the CA at all.
The unwind: 2023 to 2025
The end was a sequence of decisions that removed OCSP’s reasons to exist. In August 2023 the CA/Browser Forum voted to make OCSP optional for CAs and to make CRLs mandatory, inverting the long-standing requirement; Microsoft updated its Root Program in a similar direction in October 2024. Once OCSP was optional and CRLs were required, the largest CAs had a clear path to drop the responders they had always found expensive.
Let’s Encrypt took it. In December 2024 it published a schedule. On 30 January 2025, new OCSP must-staple issuance would start failing for accounts that had not previously used it. On 7 May 2025, certificates would stop carrying OCSP responder URLs and start carrying CRL URLs instead. On 6 August 2025, the responders would shut down. The scale being retired was enormous: at peak the service handled on the order of 340 billion OCSP requests a month, more than 140,000 per second at the CDN edge and around 15,000 per second reaching the origin, an infrastructure Let’s Encrypt argued was not making anyone safer. By the August shutdown every certificate carrying an OCSP URL had already expired, because Let’s Encrypt had stopped embedding those URLs more than ninety days earlier and its certificates are short-lived. The replacement was the mechanism OCSP had been invented to escape: CRLs, which Let’s Encrypt had quietly begun publishing back in September 2022.
*The OCSP unwind. The August 2023 CA/Browser Forum vote made the responders optional; everything after it followed.*CRLite and CRLSets: CRLs, pushed
If browsers stopped doing live revocation checks, and OCSP is being retired, what is actually catching revoked certificates in 2026? The answer is that CRLs came back, not as files browsers fetch inline but as data the browser vendor aggregates and pushes. Two systems are worth knowing.
Chrome’s CRLSets are the older and simpler one. Google crawls revocation sources, selects a subset of revocations it considers worth blocking, packs them into a compact set, and ships that set to Chrome through the component-updater channel. It is fast and it is deliberately incomplete, an emergency brake for high-value revocations rather than a full accounting of every revoked leaf.
Mozilla’s CRLite goes for completeness. The idea, first published by researchers at IEEE S&P 2017, is to take every revocation visible in Certificate Transparency logs and every unexpired certificate in those logs, and compress the membership question, “is this certificate revoked?”, into a probabilistic structure small enough to push to every browser. The original design used a cascade of Bloom filters: a first filter that may have false positives, a second filter that catches exactly those false positives, a third that catches the second’s, and so on until the false positives run out, so the cascade as a whole answers exactly with no errors. Firefox downloads this structure, stores it locally, refreshes it on a schedule, and answers every revocation question from local data, leaking nothing to anyone.
*A cascading filter answers "is this revoked?" exactly, by stacking filters until no false positive survives. CRLite builds one from Certificate Transparency.*The reason CT logs are the input is that they already aggregate every publicly-trusted certificate, which is the same data a comprehensive revocation system needs; how CT logs work and what they reveal covers that pipeline. The newer CRLite implementation, which Mozilla calls Clubcard, swaps the multi-level Bloom cascade for a partitioned two-level cascade of Ribbon filters and was presented at IEEE S&P 2025. The numbers are why the approach works at all: a Firefox client pulls roughly 300 kB of revocation data a day, built from about a 4 MB snapshot every 45 days plus deltas, which Mozilla puts at roughly a thousand times less bandwidth than downloading the underlying CRLs directly. CRLite was enabled for all Firefox desktop users in Firefox 137, and Firefox 142 turned OCSP off for domain-validated certificates, which is the browser side of the same retirement Let’s Encrypt drove on the CA side.
The structure under CRLite is the same family of probabilistic membership test that crawlers use for the URL-seen problem; if the cascade idea is new to you, Bloom filters and the URL-seen problem walks the single-filter version first.
The fingerprint angle: what the revocation layer still says about a connection
For someone watching connections rather than running a CA, the revocation layer is a small but real source of signal, and it is shifting fast enough that the signal is mostly about era and stack. A client that offers status_request in its ClientHello is announcing it will accept a stapled response, and the presence, absence, and exact placement of that extension is part of the ClientHello byte pattern that JA3 and JA4 hash. Real browsers offer it. Many HTTP libraries and impersonation tools historically did not, or offered it in a different position, which is one more way an automated client’s handshake fails to line up with the browser it claims to be. TLS fingerprinting from ClientHello bytes to JA4 covers how that extension list gets turned into a label.
There is a second-order behavioral tell too, on the client’s response to what comes back. A genuine 2026 browser stack does not block on a live OCSP fetch for a normal certificate, because both major engines have moved revocation off the connection path: Chrome onto CRLSets, Firefox onto CRLite, with OCSP disabled by default. A client that still fires an outbound OCSP request to a CA responder mid-handshake is behaving like older software or a hand-rolled validator, not like a current browser. The exact treatment of a stapled response (whether a given client validates the staple’s signature and freshness at all, versus ignoring it) is not uniformly documented across stacks, so this is a weaker signal than the ClientHello bit, and worth treating as inference rather than fact. What is solid is the direction: the more a connection’s revocation behavior looks like a live client-to-CA OCSP exchange, the more it looks like something other than a modern browser.
What the death of OCSP actually settled
The revocation story is usually told as a failure, and the protocol-level facts support that reading: OCSP leaked who browsed where, never solved the soft-fail problem it inherited from CRLs, and got turned off. But the thing that replaced it is not new. It is the CRL, the mechanism OCSP was invented to escape, returned in a form that fixes the size problem OCSP was supposed to fix. The web spent twenty years routing around whole-list download, only to land on whole-list download done centrally and pushed to clients as a compressed filter. The CA aggregates, the browser vendor compresses, the client queries locally and tells no one. That arrangement gives you completeness, privacy, and no critical-path round trip at once, which is the combination plain OCSP could never assemble.
What got settled, concretely, is that real-time revocation checking from the client is over for the public web. A modern browser in 2026 does not phone a CA when you open a page, and the largest CA no longer runs a responder to answer if it did. The leg that leaked is gone because the question that required it is now answered from a local file that was built, in the end, out of the certificate transparency logs the same ecosystem had already agreed to keep.
Sources & further reading
- Let’s Encrypt (2024), Ending OCSP Support in 2025 — the shutdown schedule and the privacy and cost rationale, straight from the CA that drove the change.
- Let’s Encrypt (2025), OCSP Service Has Reached End of Life — the final shutdown notice, with the 340-billion-request-a-month traffic figures.
- IETF (2013), RFC 6960: X.509 Internet Public Key Infrastructure Online Certificate Status Protocol — the OCSP request/response and CertID definitions, including the good/revoked/unknown statuses and the nonce.
- IETF (2015), RFC 7633: X.509v3 TLS Feature Extension — the must-staple extension, the id-pe-tlsfeature OID, and the logic of mandatory stapling.
- IETF (2011), RFC 6066: Transport Layer Security Extensions: Extension Definitions — the status_request extension that carries a stapled OCSP response in the handshake.
- Adam Langley (2012), Revocation checking and Chrome’s CRL — the seat-belt argument against soft-fail, the OCSP latency numbers, and the case for CRLSets.
- Cloudflare (2017), High-reliability OCSP stapling and why it matters — the engineering needed to make stapling reliable, and the catalogue of CA responder failure modes.
- Mozilla (2025), CRLite: Fast, private, and comprehensive certificate revocation checking in Firefox — the Clubcard/Ribbon-filter design, bandwidth figures, and the Firefox 137/142 rollout.
- Mozilla Security (2020), Introducing CRLite: All of the Web PKI’s revocations, compressed — the original cascading-Bloom-filter design and the CT-log-based construction.
- Feisty Duck (2025), The Slow Death of OCSP — the ecosystem history: the CA/Browser Forum 2023 vote, Microsoft’s root program change, and the browser positions on must-staple.
- Jeff Johnson (2022), Apple reneged on OCSP privacy — the plaintext ocsp.apple.com leak, the move to ocsp2.apple.com, and the opt-out preference that never shipped.
- TidBITS (2020), Apple Network Failure Destroys an Afternoon of Worldwide Mac Productivity — the November 2020 trustd/OCSP outage as it happened, the soft-fail-that-wasn’t case study.
Further reading
Certificate transparency: how CT logs work and what they reveal
Traces how Certificate Transparency turns CA mis-issuance into a public, append-only Merkle-tree record: SCTs, the gossip and audit model, how browsers enforce it, and why the same logs hand attackers a free subdomain map.
·23 min readThe Web PKI trust store: how browsers decide which CAs to trust
Traces how Mozilla, Apple, Microsoft, and Chrome curate the root CAs that anchor every HTTPS connection, the governance machinery behind inclusion and removal, and the Symantec, TrustCor, and Entrust distrust events that show the system enforcing itself.
·22 min readmTLS and client certificates: the strongest fingerprint of all
How mutual TLS works at the message level, the CertificateRequest, Certificate, and CertificateVerify exchange in TLS 1.3, where client certificates are deployed, and why a private key beats every behavioral signal.
·21 min read