Servers & Hosting/Guide

HTTPS done properly: TLS settings, HSTS and certificates that renew

A padlock in the browser is the start, not the finish. This guide covers TLS 1.2 and 1.3, sensible ciphers, HSTS and preloading, and the shrinking certificate lifetimes that make automation mandatory.

High impact5 min readAdmins, DevelopersUpdated 31 October 2026

Fingerprint of this page: sha256("/servers/https-tls-configuration/")

Getting a certificate is easy today. Getting HTTPS right takes a few more decisions: which protocol versions to allow, how to stop browsers from ever falling back to plain HTTP, and how to make sure the certificate renews itself long before anyone notices. This guide walks through each decision with settings you can test.

Protocols and ciphers: TLS 1.2 and 1.3 only

TLS 1.0 and 1.1 were formally deprecated by the IETF in RFC 8996, and current browsers no longer accept them. Allow TLS 1.2 and TLS 1.3 only. TLS 1.3 removed the old algorithms entirely, so there is little to configure. TLS 1.2 still permits weak choices, so limit it to cipher suites with (ECDHE key exchange) and authenticated encryption (AES-GCM or ChaCha20-Poly1305).

You do not need to build that list yourself. Mozilla's SSL configuration generator turns its server side TLS guidance into ready-made settings for common servers, in three profiles:

  • Intermediate: TLS 1.2 and 1.3 with the cipher suites above. The right default for almost every public website.
  • Modern: TLS 1.3 only. Fits APIs and services where you control every client.
  • Old: for very old clients only, and then as a deliberate, temporary exception.

The current version of the guidance also lists the hybrid post-quantum key exchange X25519MLKEM768 first. Use it only where your TLS library supports it; on older libraries a group the library does not know stops the server from starting, so test before you copy.

nginxNginx server block, intermediate profile
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:TLS:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # Start low, raise to 63072000 (two years) once everything works
    add_header Strict-Transport-Security "max-age=86400" always;

    root /var/www/example;
}
apacheApache virtual host for port 443 (needs mod_ssl and mod_headers)
SSLEngine on
SSLCertificateFile    /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

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

Header always set Strict-Transport-Security "max-age=86400"

Caddy picks modern settings on its own. For Nginx and Apache, run nginx -t or apachectl configtest before every reload, and keep in mind that in Nginx an add_header inside a location block replaces all headers set at server level.

Redirect HTTP to HTTPS

Keep port 80 open, but only to redirect. Visitors who type your domain without a scheme and old links still arrive there, and clients often prove domain control over port 80. Send a permanent redirect to the same host on HTTPS and serve nothing else.

nginxNginx: port 80 only redirects
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

HSTS and the preload list

A redirect still leaves one plain request that an attacker on the same network could intercept. , sent as the Strict-Transport-Security header, closes that gap: once a browser has seen it, the browser uses HTTPS for your domain for as long as max-age says, without trying HTTP first.

includeSubDomains extends the rule to every subdomain. That is what you want, but only once every subdomain serves valid HTTPS, including the old intranet, the webmail login and the printer page someone exposed years ago. The preload directive goes further: you submit your domain to a list built into browsers, so even the very first visit uses HTTPS. The list requires a max-age of at least one year, includeSubDomains, preload and a redirect from HTTP to HTTPS on the same host.

Certificates: automate, because lifetimes are shrinking

The CA/Browser Forum sets the rules public certificate authorities follow. Under ballot SC081, the maximum lifetime of a public TLS certificate fell from 398 to 200 days on 15 March 2026. It drops to 100 days on 15 March 2027 and to 47 days on 15 March 2029. The period for which a CA may reuse a domain validation shrinks in step, to 10 days in 2029. Renewing by hand is no longer realistic.

ACME (RFC 8555) solves this. An ACME client on your server proves control of the domain, fetches the certificate and renews it on schedule. Let's Encrypt certificates last 90 days by default today. The project has announced a move to 64 days in February 2027 and to 45 days in February 2028. It recommends renewal through ACME Renewal Information (ARI) where your client supports it, or at about two thirds of the lifetime, instead of a fixed 60-day cycle.

bashCertbot: check that renewal really works
# Simulate a renewal against the staging environment
sudo certbot renew --dry-run

# Confirm a timer runs the renewal
systemctl list-timers | grep -i certbot

OCSP, stapling and revocation

Browsers once checked revocation through , asking the certificate authority whether a certificate was still valid. That leaked browsing data to the CA and often failed quietly. Since 2024 the Baseline Requirements make OCSP optional and require CRLs, and Let's Encrypt shut its OCSP service down on 6 August 2025.

In practice: with a CA that no longer runs OCSP, stapling has nothing to staple, so leave ssl_stapling off, which is already the Nginx default. If your CA still runs OCSP, stapling is harmless and saves clients a lookup. Either way, revocation is not your main safety net. Short lifetimes and a private key that never leaves the server are.

Mixed content and testing

A page served over HTTPS that loads scripts, images or styles over plain HTTP contains . Browsers block mixed scripts and upgrade or block other mixed resources, so pages break in odd ways. Search templates and the database for references to your own domain that start with http: and replace them. During a migration, a Content-Security-Policy with upgrade-insecure-requests can bridge the gap.

Test from outside after every change. A public TLS scanner shows protocols, ciphers and the certificate chain in one report. From the command line you can check the essentials yourself:

bashQuick checks from any machine
# Both must connect and show your certificate chain
openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null

# Must print the HSTS header
curl -sI https://example.com | grep -i strict-transport-security

HTTPS done

0 of 9 done

Your ticks stay in this browser.

Short lifetimes and a private key that never leaves the server are your real safety net.

Sources and further reading

  1. CA/Browser Forum: TLS Baseline Requirementscabforum.org
  2. Let's Encrypt: Decreasing certificate lifetimes to 45 daysletsencrypt.org
  3. Let's Encrypt: OCSP service has reached end of lifeletsencrypt.org
  4. HSTS preload list submissionhstspreload.org
  5. Mozilla SSL configuration generatorssl-config.mozilla.org
  6. Nginx documentation: ngx_http_ssl_modulenginx.org

General information, checked on 31 October 2026. Test changes on one system first and keep a way back. Found something outdated? Tell the editors.

FAQ

Questions readers ask

Can I offer TLS 1.3 only?

For APIs and internal services where you control the clients, yes. For a public website, some older clients and integrations still need TLS 1.2, so the intermediate profile with both versions is the safer default.

Are free certificates less secure than paid ones?

No. Domain validated certificates from free and paid certificate authorities use the same cryptography and are trusted the same way by browsers. Paid products differ in validation type, support and service, not in encryption strength.

Should I use ECDSA or RSA certificates?

ECDSA keys are smaller and faster, while RSA keys of 2048 bits or more still work everywhere. Mozilla's intermediate profile allows both, and many servers serve one of each. For modern clients, ECDSA with P-256 is a good choice.

What happens when a certificate expires?

Browsers show a full-page warning and most visitors leave. API clients and apps usually refuse the connection outright, so integrations fail without anyone seeing an error page. That is why expiry monitoring matters as much as automated renewal.

Keep reading

More from Servers & Hosting