OCSP Stapling and Modern TLS Config
Introduction¶
A TLS config from three years ago probably still "works" but leaves capability and security on the table. This is a current baseline plus OCSP stapling, which removes a latency hit and privacy leak from certificate revocation checks.
Protocols and ciphers¶
Support TLS 1.2 and 1.3 only. TLS 1.3's cipher suites are fixed and good; for 1.2, use a modern list (Mozilla "intermediate").
Nginx¶
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off; # let 1.3 clients choose; fine for 1.2 too with a good list
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
Apache¶
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...
SSLHonorCipherOrder off
SSLOpenSSLConfCmd Curves X25519:prime256v1:secp384r1
Use an ECDSA certificate (P-256) — smaller, faster handshakes than RSA; keep an RSA cert alongside only if you must support ancient clients (Nginx/Apache serve both by SNI).
OCSP stapling¶
Normally a client checks if your cert is revoked by contacting the CA's OCSP responder — adding latency and telling the CA which sites the user visits. Stapling has your server fetch a signed OCSP response periodically and attach ("staple") it to the handshake. Client verifies it locally.
Nginx¶
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem; # the issuer chain
resolver 1.1.1.1 9.9.9.9 valid=300s; # Nginx needs to resolve the OCSP responder
resolver_timeout 5s;
Nginx fetches lazily on the first handshake after startup — the very first
client may not get a staple. Newer Nginx (1.27+) has proactive fetching /
ssl_stapling_file for a pre-fetched response.
Apache¶
SSLUseStapling on
SSLStaplingCache "shmcb:/var/run/ocsp(128000)"
SSLStaplingReturnResponderErrors off
OCSP Must-Staple¶
You can request a cert with the must-staple extension: clients then reject the connection if a valid staple isn't present. Strong, but risky — if your stapling breaks, your site goes down. Only enable it if your stapling is monitored and reliable.
certbot ... --must-staple # bakes the extension into the CSR
Session resumption¶
Lets a returning client skip the full handshake.
ssl_session_cache shared:SSL:10m; # ~40k sessions per 10m; shared across workers
ssl_session_timeout 1d;
ssl_session_tickets off; # OFF unless you rotate ticket keys — see below
- Session cache (server-side) — safe, but doesn't scale across a fleet without a shared store.
- Session tickets — stateless (client holds the encrypted state), scales,
but if the ticket key is long-lived it undermines forward secrecy. Either
disable tickets, or rotate the ticket key frequently (hourly) across all
servers (
ssl_session_ticket_keyfiles, rotated by a cron/secret system). TLS 1.3 tickets are single-use and less problematic.
HSTS¶
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
Only add preload (and submit to hstspreload.org) once you're certain every
subdomain is HTTPS forever — it's hard to undo. See
the HSTS preload tool.
Verification and troubleshooting¶
# Full audit
testssl.sh https://example.com
# OCSP staple check
openssl s_client -connect example.com:443 -status -servername example.com </dev/null 2>&1 | grep -A17 'OCSP response'
# "OCSP Response Status: successful (0x0)" = stapling works
curl -sI https://example.com | grep -i strict-transport
OCSP response: no response sent— Nginx can't reach the responder (noresolverconfigured, or egress blocked),ssl_trusted_certificateis wrong (needs the issuer chain, not your leaf), or the first request triggered the lazy fetch and it hasn't completed. Hit the site a few times.ssl_stapling ignored, issuer certificate not found— pointssl_trusted_certificateatchain.pem/fullchain.pem(Let's Encrypt).- Must-staple site down — stapling broke and clients now hard-fail. Remove must-staple (reissue the cert) or fix the responder path fast.
- Old clients can't connect after dropping TLS 1.0/1.1 — that's intended; check your analytics for real impact first. A tiny RSA-cert fallback + TLS 1.2 covers almost everything.
SSLHonorCipherOrder/ssl_prefer_server_ciphers— with a modern list,offis fine and lets fast clients pick ChaCha20 on mobile.ononly if you must force a specific order for compliance.- Grade capped at B on SSL Labs — usually missing forward secrecy (old
cipher list), TLS 1.0/1.1 still on, weak DH params (use ECDHE, not DHE; or
generate 2048+ bit
dhparamif you keep DHE), or no HSTS.
Related tools and reading¶
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Web & Mail Servers
Running BIND 9 as an Authoritative Server
BIND as an authoritative server: zone files, secured AXFR with TSIG, SOA serial rules, and DNSSEC inline-signing. Here's a working master/slave setup.
October 24, 2026Varnish Cache in Front of a Site
Varnish is a caching HTTP accelerator configured in VCL. Here's the request lifecycle, why you strip cookies, how bans work, and grace mode for serving stale during backend trouble.
October 24, 2026Let's Encrypt with Certbot, Including Renewal That Doesn't Break
Getting the first cert is easy; keeping renewal working for years is the part people get wrong. Webroot vs DNS-01, deploy hooks, and the failure modes.
October 24, 2026