WhoDNS
All articles

TLS Hardening: Grading Your Server and Fixing the Config

A TLS configuration ages badly. The server you hardened in 2020 still works, but it may now offer protocols and cipher suites that auditors flag and attackers scan for. A TLS scan lists exactly what your endpoint negotiates, so hardening becomes a checklist instead of an opinion.

What the scan reports

  • Protocol support: TLS 1.0 and 1.1 are formally deprecated (RFC 8996) and must be off. TLS 1.2 remains acceptable with good ciphers; TLS 1.3 should be on — it removes entire classes of weak options by design.
  • Cipher suites: anything CBC-based on TLS 1.2 without an AEAD alternative is suspect; RC4, 3DES, and all export-grade suites are disqualifying. On TLS 1.3 the suite list is short and all three options are fine.
  • Key exchange and certificates: RSA keys under 2048 bits, SHA-1 signatures, and missing forward secrecy all surface here.
  • Extras: HSTS presence, OCSP stapling, session resumption behaviour, and whether the certificate chain is complete — a missing intermediate that browsers tolerate via cached chains still fails strict clients.

Grading and interpreting

Scanners assign letter grades; treat them as a trend, not a religion. The path to a solid grade is nearly always the same three moves: disable old protocols, order and restrict ciphers, enable TLS 1.3. Verify after each change rather than assuming:

openssl s_client -connect example.com:443 -tls1_2 < /dev/null
openssl s_client -connect example.com:443 -tls1 < /dev/null

The first should complete a handshake; the second must fail with a protocol alert. That pair of tests is the honest minimum for “old TLS is really off”.

Fixing the config

A defensible nginx baseline:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
add_header Strict-Transport-Security "max-age=31536000" always;

The equivalent Apache directives are SSLProtocol and SSLCipherSuite with the same values, plus SSLOpenSSLConfCommands for curves. Two details people get wrong: ssl_prefer_server_ciphers off is correct for TLS 1.3-era configs (clients choose better than old guidance assumed), and HSTS belongs on port 443 vhosts only — sending it over HTTP is meaningless and, worse, the header cannot be removed by an HTTP redirect.

Where certificate transparency fits

CT logs record every publicly trusted certificate. After enabling issuance for a domain, watch the logs for certificates you did not request: CAA records plus CT monitoring catch mis-issuance early, and “unexpected cert in CT” is a classic early indicator of a compromised CA account or an inside job.

Scan the endpoint before and after every change with the TLS scanner — it reports protocol versions, negotiated ciphers and certificate details in one pass, so you can confirm the fix took effect on the public side of the load balancer, not just on the config file you edited.

TLS scanTLS 1.3weak cipherscipher suitesnginx ssl configapache sslcertificate transparencyserver grading