Hardening TLS
Getting a certificate is step one. What you negotiate with it is where the actual security lives, and the defaults on older systems are not good.
Protocol versions
TLS 1.2 and 1.3. Nothing else.
ssl_protocols TLSv1.2 TLSv1.3;
1.0 and 1.1 are deprecated, fail compliance scans and are not needed by any browser in current use. SSLv3 has been broken for a decade.
Ciphers
TLS 1.3 handles this itself; its cipher suites are all good. For 1.2, a short modern 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_prefer_server_ciphers off;
Forward secrecy only, AEAD only. Letting the client choose is now the better default: a phone without AES hardware picks ChaCha20 and is faster for it.
Sessions and stapling
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
Tickets off unless you rotate the keys, because a static ticket key undermines forward secrecy.
ssl_stapling on;
ssl_stapling_verify on;
Stapling means your server presents the revocation status itself, so the client does not make a separate request to the CA. Faster, and more private for your visitors.
HSTS, deliberately
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"
always;
This tells browsers never to use plain HTTP for your domain again, and it is hard to undo — a visitor's browser remembers it for a year. Enable it only once every subdomain genuinely works over HTTPS. Start with a short max-age if you are unsure.
And remember that one add_header in a location block cancels inherited
headers; see nginx pitfalls.
Test from outside
Do not trust the configuration file. Check what is actually negotiated:
openssl s_client -connect example.com:443 -tls1_1 — should
fail
echo | openssl s_client -connect example.com:443 -servername example.com
2>/dev/null | openssl x509 -noout -subject -enddate
A public SSL testing service gives you the full picture including chain problems. Be sceptical of anything reporting a protocol as enabled when a handshake actually failed — a “Cipher is (NONE)” result means refused, not supported.
The chain and the renewal
Serve the full chain, not just the leaf: browsers forgive a missing intermediate, many API clients and mobile apps do not. And the most common TLS outage is not a weak cipher, it is an expired certificate — monitor the expiry date and test the renewal, as TLS certificates describes.
Still stuck? Mail support@novogara.com — an engineer answers, at any hour. Back to the knowledge base