A certificate alone doesn’t make HTTPS secure. The server also needs sensible TLS settings: which protocol versions it accepts, which ciphers it offers, and how sessions are handled. Old defaults (TLS 1.0/1.1, weak ciphers) get flagged by scanners and some compliance checks. Here are modern settings for Nginx and Apache, and how to verify them.

Requirements

TLS 1.3 needs OpenSSL 1.1.1 or newer. Every current distribution has OpenSSL 3.x:

openssl version
nginx -V 2>&1 | grep -o 'built with OpenSSL [0-9.]*'

Nginx

Put shared settings in the http block, for example /etc/nginx/conf.d/tls.conf, so every server block inherits them:

ssl_protocols TLSv1.2 TLSv1.3;

# TLS 1.2 ciphers (TLS 1.3 suites are secure by default and configured separately)
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;

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 9.9.9.9 valid=300s;
resolver_timeout 5s;

In each HTTPS server block:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;                      # Nginx 1.25.1+; older: "listen 443 ssl http2;"
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    …
}
sudo nginx -t && sudo systemctl reload nginx

Notes:

  • Certbot’s include (/etc/letsencrypt/options-ssl-nginx.conf) sets similar values. Don’t define the same directives twice in one context, or nginx -t complains.
  • ssl_prefer_server_ciphers off — with only strong ciphers offered, letting clients choose lets mobile devices without AES hardware pick ChaCha20.
  • Session tickets off keeps forward secrecy strict, unless you rotate ticket keys. Session caching still provides fast resumption.
  • OCSP stapling lets the server include the certificate’s revocation status, saving clients a lookup. Let’s Encrypt has been phasing out OCSP in favour of CRLs, so stapling may simply have no effect for those certificates. It’s harmless to leave on.

See HTTP/2 and HTTP/3 in Nginx.

Apache

In /etc/apache2/mods-available/ssl.conf (Debian/Ubuntu), /etc/httpd/conf.d/ssl.conf (RHEL), or inside each <VirtualHost *:443>:

SSLProtocol             -all +TLSv1.2 +TLSv1.3
SSLCipherSuite          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
SSLHonorCipherOrder     off
SSLSessionTickets       off

SSLUseStapling On
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"     # must be outside <VirtualHost>

Protocols h2 http/1.1
sudo apachectl configtest && sudo systemctl reload apache2

Protocols h2 needs mod_http2 and the event or worker MPM. See Apache MPM event.

The Mozilla SSL Configuration Generator

Mozilla maintains recommended configs (Modern, Intermediate, Old) for Nginx, Apache, HAProxy and others, tailored to your server and OpenSSL versions. Intermediate (TLS 1.2 + 1.3) is right for public websites. Modern (TLS 1.3 only) suits APIs and services whose clients are all up to date.

ECDSA certificates

ECDSA keys are smaller and faster than RSA, with equal security. Certbot can issue them:

sudo certbot --nginx --key-type ecdsa -d example.com

Some setups serve both RSA and ECDSA certificates for maximum compatibility. For most sites today, ECDSA alone is fine.

Test your configuration

From the command line:

openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>/dev/null | grep -E 'Protocol|Cipher'
openssl s_client -connect example.com:443 -tls1_1 </dev/null      # should FAIL
nmap --script ssl-enum-ciphers -p 443 example.com

See openssl commands and nmap.

Online: Qualys SSL Labs’ server test grades the configuration (aim for A or A+ with HSTS) and shows which clients can connect. testssl.sh is a thorough command-line alternative you can run yourself.

After the protocol: HSTS

Once HTTPS works everywhere, tell browsers to always use it:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Understand the consequences first. See HSTS explained.

Quick reference

Setting Nginx Apache
Versions ssl_protocols TLSv1.2 TLSv1.3; SSLProtocol -all +TLSv1.2 +TLSv1.3
Ciphers ssl_ciphers …; SSLCipherSuite …
Client preference ssl_prefer_server_ciphers off; SSLHonorCipherOrder off
Session tickets ssl_session_tickets off; SSLSessionTickets off
Stapling ssl_stapling on; SSLUseStapling On
HTTP/2 http2 on; Protocols h2 http/1.1

Related: SSL/TLS explained and Nginx security headers.

  • SSL
  • Nginx
  • Security