dnsprobe v1

OCSP stapling explained

· ~3 min read · dnsprobe.net/blog

OCSP (Online Certificate Status Protocol, RFC 6960) is how a client checks whether a server's certificate has been revoked. The naive flow has the client query the CA's OCSP responder directly. OCSP stapling rearranges that flow so the server queries the responder on the client's behalf and includes the signed result in its TLS handshake. The improvement in latency and privacy is substantial. The implementation is full of operational landmines.

The original problem

A certificate carries a notAfter date and a serial number. Before notAfter, the only way to know whether the cert is still valid is to consult the CA. Two mechanisms existed historically:

  1. CRL (Certificate Revocation List). The CA publishes a signed list of revoked serial numbers. Clients download it and check. CRLs grew to multi-megabyte sizes in the 2000s, making them impractical to fetch on every TLS connection.
  2. OCSP. The CA exposes an HTTP endpoint that answers "is serial number X still valid?" with a small signed response. Clients query before completing the handshake.

The OCSP-on-every-connection model had three problems:

  • Latency. The client adds a synchronous request to the CA's OCSP responder before the handshake completes.
  • Privacy. The CA learns which sites every client visits, in close-to-real-time.
  • Availability. If the OCSP responder is down or slow, every connection that requires a fresh check stalls.

The practical response from browsers was "soft fail" — if the OCSP check times out, treat the cert as valid anyway. Which means an attacker who can also break the OCSP query path effectively neutralises OCSP entirely. Chrome eventually stopped doing live OCSP checks for non-EV certs as a result.

OCSP stapling, the rearrangement

OCSP stapling (RFC 6066's certificate status request extension) inverts the model:

  1. The server periodically queries the CA's OCSP responder for the status of its own cert.
  2. The CA returns a signed response valid for some period (often 4-7 days).
  3. The server caches that response.
  4. When a client connects with the status_request extension in the ClientHello, the server attaches ("staples") the cached OCSP response to the certificate message.
  5. The client validates the OCSP response (it is signed by the CA), accepts it, and proceeds with the handshake.

One CA round trip per few days instead of one per client connection. No client-to-CA query. No latency tax. And the OCSP response is cryptographically fresh — the client can verify the signature and reject expired responses.

Configuration

nginx:

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

Three subtle requirements:

  • ssl_stapling_verify on — nginx will not staple a response it cannot itself verify. Skip this and a stale or wrong response can be served to clients.
  • ssl_trusted_certificate — the chain file used to verify the OCSP responder's certificate.
  • resolver — nginx needs to resolve the OCSP responder's hostname. Without this directive nginx never fetches a response and stapling silently does nothing.

The silent failure mode

After a config change or restart, nginx will not have a cached OCSP response yet. Until the first successful background fetch, clients connecting do not get a stapled response. The handshake still succeeds because OCSP stapling is optional — but until the cache is warm, you get the worst of both worlds.

Verify a config is actually stapling:

echo Q | openssl s_client -connect example.com:443 -servername example.com -status 2>/dev/null \
  | grep -A1 "OCSP response"
OCSP response:
======================================
OCSP Response Data:
    OCSP Response Status: successful (0x0)

If you see "OCSP response: no response sent", stapling is not working. Check nginx error logs for "OCSP responder did not respond" or "OCSP_basic_verify() failed".

Must-staple

The Must-Staple TLS extension (a flag in the certificate itself, RFC 7633) tells clients: "this cert MUST be served with a stapled OCSP response. If you do not see one, refuse the connection". This closes the soft-fail loophole.

Let's Encrypt supports Must-Staple via the --must-staple flag to certbot. The trade-off: any stapling outage now becomes a customer-facing outage. If you cannot guarantee 99.9%+ stapling availability, do not enable Must-Staple.

Browser behaviour, current state

Chrome does not do live OCSP at all for non-EV certs; it relies on a CRLSet pushed via the browser update channel. Firefox does live OCSP by default and respects Must-Staple. Safari uses live OCSP with soft-fail. The practical effect of stapling on Chrome is mostly the privacy and CA-load wins, not security; on Firefox it is meaningful.

To see whether a specific host actually staples, the SSL panel on dnscheck reports OCSP stapling status alongside the certificate details. Try mozilla.org for a host that runs the stack end-to-end correctly.

References: RFC 6960, RFC 6066, RFC 7633.