Server

OCSP Stapling and Modern TLS Config

By Prabath Thalangama· October 24, 2026· 4 min read
#tls#nginx#security

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_key files, 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 (no resolver configured, or egress blocked), ssl_trusted_certificate is 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 — point ssl_trusted_certificate at chain.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, off is fine and lets fast clients pick ChaCha20 on mobile. on only 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 dhparam if you keep DHE), or no HSTS.
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.