Enabling OCSP Stapling in OpenSSL TLS Servers
Learn how to enable OCSP stapling in an OpenSSL TLS server, see a minimal C callback example, and verify the stapled response with openssl s_client.
20 Jun 2026, 16:33 UTC

Useful answer
To improve TLS handshake performance and privacy, configure an OpenSSL‑based server to fetch a time‑stamped OCSP response for its certificate and return it during the handshake using the status_request extension (OCSP stapling).
How it works – a worked configuration
The server must provide a callback that obtains the OCSP response from the CA’s responder and hands it to OpenSSL. The example below shows a minimal C callback that fetches the response via HTTP (you can replace the fetch logic with any method that returns a DER‑encoded OCSP response).
#include
#include
#include
static int ocsp_status_callback(SSL *ssl, void *arg)
{
const unsigned char *resp = NULL;
size_t resp_len = 0;
/* 1. Load the server certificate from the SSL object */
X509 *cert = SSL_get_certificate(ssl);
if (!cert) return SSL_TLSEXT_ERR_NOACK;
/* 2. Build an OCSP request for the certificate */
OCSP_REQUEST *req = OCSP_REQUEST_new();
OCSP_cert_to_id(NULL, cert, NULL); /* simplified – use proper issuer lookup */
/* ... fill request ... */
/* 3. Send request to OCSP responder (replace URL with your CA’s responder) */
CURL *curl = curl_easy_init();
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/ocsp-request");
curl_easy_setopt(curl, CURLOPT_URL, "http://ocsp.example.com/");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->data);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->length);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
/* Capture response */
struct { unsigned char *buf; size_t len; } mem = {0};
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, [](void *ptr, size_t size, size_t nmemb, void *data) -> size_t {
auto *m = static_cast(data);
size_t realsize = size * nmemb;
m->buf = (unsigned char*)realloc(m->buf, m->len + realsize + 1);
memcpy(m->buf + m->len, ptr, realsize);
m->len += realsize;
m->buf[m->len] = 0;
return realsize;
});
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
if (res != CURLE_OK) return SSL_TLSEXT_ERR_NOACK;
/* 4. Return the OCSP response to OpenSSL */
resp = mem.buf;
resp_len = mem.len;
SSL_set_tlsext_status_ocsp_resp(ssl, resp, resp_len);
free(mem.buf);
return SSL_TLSEXT_ERR_OK;
}
void setup_ssl_context(SSL_CTX *ctx)
{
SSL_CTX_set_tlsext_status_cb(ctx, ocsp_status_callback);
SSL_CTX_set_tlsext_status_arg(ctx, NULL);
/* optional: require clients to send status_request */
SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
}
If you prefer to test stapling without writing code, OpenSSL’s built‑in server can enable it with a single flag:
openssl s_server -cert server.pem -key key.pem -status -accept 4430
The -status flag tells s_server to act as a stapling‑capable server; it will automatically fetch the OCSP response from the responder defined in the certificate’s Authority Information Access extension.
Limits and operational considerations
- Outbound connectivity – The server must be able to reach the OCSP responder (typically HTTP on port 80 or HTTPS on 443). Firewall rules blocking this path cause the callback to fail, and the server will send an empty
status_requestextension, leading clients to treat the certificate as unverified. - Response lifetime – OCSP responses are usually valid for a few hours. If the cached response expires, the callback must refresh it; otherwise the server will again return no response.
- OpenSSL version – The stapling API (
SSL_CTX_set_tlsext_status_cb) is complete starting with OpenSSL 1.0.2. Older versions either lack the callback or provide only a partial implementation. - Client side – The client must send the
status_requestextension (most modern browsers do). If the client does not, stapling is invisible but harmless.
Common mistakes
- Using a callback that returns
SSL_TLSEXT_ERR_NOACKor an empty response when the OCSP fetch fails. This causes the handshake to proceed without a stapled response, which may trigger soft‑fail revocation checks in strict clients. - Forgetting to enable the
status_requestextension on the server side (e.g., omitting-statuswiths_serveror not setting the callback). The server will never send an OCSP response, even if the client asks for it. - Assuming that the presence of an OCSP URL in the certificate guarantees stapling will work. If the server cannot contact that URL (DNS, proxy, or firewall), stapling fails silently.
- Refreshing the OCSP response too infrequently. A stale response leads to a
Response Verify Result: 1 (revoked)or2 (unknown)when the client validates it.
Practical verification
After deploying the server, confirm that stapling is active:
# From a client machine
openssl s_client -connect yourserver.example.com:443 -status
Look for lines similar to:
OCSP Response Data:
OCSP Response Status: successful (0x0)
Response Type: Basic OCSP Response
Version: 1 (0x2)
Responder ID: ...
Produced At: ...
Responses:
Certificate ID:
Hash Algorithm: sha1
Issuer Name Hash: ...
Issuer Key Hash: ...
Serial Number: ...
Cert Status: good
This Update: ...
Next Update: ...
Signature Algorithm: sha256WithRSAEncryption
...
OCSP Response Data:
OCSP Response Status: successful (0x0)
Response Verify Result: 0 (good)
A Response Verify Result: 0 (good) indicates the server supplied a valid, signed OCSP response that the client could verify.
For deeper inspection, capture the TLS handshake with tcpdump or Wireshark and verify that the ServerHello contains the status_request extension and that the encrypted handshake includes the OCSP response bytes.
Rollback
Enabling OCSP stapling does not modify persistent server state beyond the configuration files or code you change. To disable it, simply remove the -status flag from s_server or unset the callback (SSL_CTX_set_tlsext_status_cb(ctx, NULL)) and restart the service. No data cleanup is required.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.