TLS security: 1.3, 200-day certs, and HSTS (2026)

A sealed teal glass tunnel. A torn coral envelope outside.

TLS is how a client knows it is talking to you and that the path is encrypted. It is not authorization on the request.

Certificate lifetime caps, old protocols, and a missing redirect still show up on real estates. HSTS is how you stop the next plaintext visit after the first HTTPS hit.

The usual mistake is an A grade on a marketing domain and TLS 1.0 still offered on the API subdomain that holds sessions.

This page is the TLS settings that still matter in 2026 and the mistakes a ‘we have HTTPS’ checkbox hides.

The CA/Browser Forum’s TLS Baseline Requirements section 6.3.2 already caps a new public Subscriber certificate at 200 days as of 15 March 2026. The same table sends that cap to 100 days on 15 March 2027 and to 47 days on 15 March 2029. Ballot SC-081v3 passed on 11 April 2025. A 398-day buy you still have in a drawer is leftover inventory, not a plan.

The control is a protocol list that starts at 1.2, a certificate you can replace in hours, a Strict-Transport-Security header the browser will honor, and a page that never mixes schemes. Pair the host work with the Ubuntu page. Pair the header work with the headers page and with Helmet if the app is Express.

TLS 1.0 and 1.1 are already dead

RFC 8996, published in March 2021, formally deprecated the two oldest TLS versions. Chrome 84 disabled both in July 2020. Firefox 78 and Safari 15 did the same in that wave. PCI DSS has required TLS 1.2 or newer for card data for years. A server that still lists TLSv1 or TLSv1.1 in 2026 is offering ciphers those RFCs already called broken: RC4, or CBC with the padding-oracle history that followed it.

A public name in August 2026 offers TLS 1.3, sends HSTS, and replaces the cert on a 90-day ACME loop. TLS 1.0 and 1.1 stay off the listener.

SecureCoding

A public name in August 2026 offers TLS 1.3, sends HSTS, and replaces the cert on a 90-day ACME loop. TLS 1.0 and 1.1 stay off the listener.

Turn them off at the listener. Do not wait for a client to “need” them. A client that cannot do 1.2 is a client you put behind a proxy you own, or a client you retire. Offering the 2012-era protocols so a scanner can connect is how you keep a downgrade door on the public name.

# BAD: any of these still on a public 443
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# SSLProtocol all -SSLv3

TLS 1.3, and Intermediate if you still need 1.2

TLS 1.3 is RFC 8446. The handshake is shorter. The cipher list is AEAD only. The protocol drops renegotiation and the old static-RSA key exchange. TLSRef Server-Side TLS, the public successor to Mozilla’s guide. Version 6.0, released 3 May 2026, removed the Old configuration entirely. Two profiles remain.

  • Modern: TLS 1.3 only. ECDSA P-256. Groups include X25519MLKEM768, then X25519 and the NIST P curves. Use this when you control the clients.
  • Intermediate: TLS 1.2 and 1.3. The recommended default for a general-purpose public site. TLS 1.2 ciphers are the ECDHE plus AES-GCM or ChaCha20-Poly1305 set. No DHE-RSA in the current list, after the D(HE)at resource-exhaustion note.

TLSRef Intermediate still lists TLS 1.2 because Android 4.4.2 and Java 8u161 remain in the compatibility table. If your access logs show no handshake below 1.3 for a quarter, move to Modern. Do not keep 1.2 “just in case” after you have measured.

200 days now, 47 days in 2029

Forum’s current requirements page and the SC-081v3 vote. Section 6.3.2 is the schedule.

FromMax validityDomain-validation reuse
15 Mar 2026200 days200 days
15 Mar 2027100 days100 days
15 Mar 202947 days10 days

Today is 22 August 2026. A new publicly trusted cert cannot be longer than 200 days. The 47-day destination is real and dated. Domain-validation reuse drops with it, and on 15 March 2029 that reuse window is 10 days. Manual revalidation at that cadence is an outage plan. Automation is the control the ballot was written to force.

TLSRef already recommends a 90-day lifespan, 90 days being the Let’s Encrypt default you should already be living on. Stay on 90 or shorter. Do not buy the 200-day maximum just because it is legal. A 90-day ACME loop is practice for 47.

Issue the cert from a cron you own

Let’s Encrypt, Google Trust Services, and ZeroSSL all speak ACME. Caddy issues and reloads as part of the binary. certbot plus a reload hook is the nginx form. The hook has to run nginx -t before reload. A failed renew must page you before the notAfter date, not after the browser interstitial.

# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/sh
set -eu
nginx -t
systemctl reload nginx
# Prove the timer exists. Expect a next run measured in days, not months.
systemctl list-timers | grep certbot
certbot certificates

Name every hostname the cert must cover in the ACME request. A leftover intranet alias that is not on the cert is a mixed-content or name-mismatch ticket the first time someone hits it. Short-lived Subscriber certificates, 7 days on or after 15 March 2026 per the Forum glossary, are the other path if your CA offers them and your stack can replace that often. requirements glossary for that 7-day figure. I did not confirm every public CA ships that product today. If yours does not, stay on 90-day ACME. Do not invent a weekly manual paste.

HSTS, then no mixed content

Strict-Transport-Security tells the browser to skip http:// on this host for max-age seconds. TLSRef sets max-age=63072000, two years, on both profiles. Helmet 8 still ships max-age=31536000; includeSubDomains, one year. Prefer the two-year value once 443 is stable. includeSubDomains is a lock on every label under the zone. Turn it on only after staging, mail, and the forgotten wiki also speak HTTPS. preload is a request to enter the browser-shipped list at hstspreload.org. Submit only after max-age is at least 31536000, includeSubDomains is on, and you can live with a year of browser updates if you need to undo it.

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

Mixed content is an HTTPS page that still fetches http:// for a script, a stylesheet, a form action, or an image. Browsers block active mixed content. Passive mixed images still leak the request and teach the page that HTTP is fine. The header that upgrades leftover HTTP subresources is Content-Security-Policy: upgrade-insecure-requests. The header that refuses them is a CSP that never lists an http: source. Do not ship http:// in a script src, an API base URL, or a form action. Search the repo for http:// and treat a hit as a defect.

nginx and Caddy that match the bar

Terminate TLS on a process you admin. The Ubuntu page is the user, the firewall, and the loopback worker. This page is the cipher line. TLSRef Intermediate on nginx 1.18+, OpenSSL 1.1.1+:

# /etc/nginx/conf.d/tls.conf
ssl_protocols TLSv1.3 TLSv1.2;
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_ecdh_curve X25519MLKEM768:X25519:prime256v1:secp384r1;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header Content-Security-Policy "upgrade-insecure-requests" always;

Modern is the same file with ssl_protocols TLSv1.3; and no ssl_ciphers line. TLS 1.3 suites are not selected with that directive. Caddy 2 on a host you own is shorter, because the binary already prefers 1.3 and issues the cert:

# /etc/caddy/Caddyfile
your-app.example {
 reverse_proxy 127.0.0.1:8080
 header {
 Strict-Transport-Security "max-age=63072000; includeSubDomains"
 Content-Security-Policy "upgrade-insecure-requests"
 }
}

If X25519MLKEM768 is unknown to your OpenSSL, Drop it from ssl_ecdh_curve and keep X25519:prime256v1:secp384r1. Do not disable 1.3 to paper over a curve name. Upgrade OpenSSL, or leave the hybrid group off until the package has it.

HTTP/80, if you open it, exists to send people to 443. It is not a second app.

server {
 listen 80;
 server_name your-app.example;
 return 301 https://$host$request_uri;
}

Do not serve the application on 80 “for old links.” The 301 plus HSTS is the path. A form that still posts to http:// is mixed content you will find with the repo search above, not with a second vhost that keeps HTTP alive.

Prove the handshake

You are not walking a downgrade against someone else’s host. You are proving your listener refused 1.0 and presented a cert that is not already stale.

  1. ssl_protocols or the Caddyfile lists 1.3, and 1.2 only if you chose Intermediate.
  2. openssl s_client against your own name with -tls1 fails. The same command with -tls1_3 prints a session.
  3. echo | openssl s_client -connect your-app.example:443 -servername your-app.example 2>/dev/null | openssl x509 -noout -dates shows a notAfter inside 90 days, not 398.
  4. A browser against your own origin shows the HSTS header and no mixed-content warning in DevTools.
# Expect this to fail. 1.0 must not negotiate.
echo | openssl s_client -connect 127.0.0.1:443 -tls1 -servername your-app.example
# Expect TLS 1.3 and a cert you issued.
echo | openssl s_client -connect 127.0.0.1:443 -tls1_3 -servername your-app.example
curl -sI "https://your-app.example/" | grep -i strict-transport-security

SSL Labs or testssl.sh against a name you own is a second opinion. A grade that still mentions 1.0 is a config you have not reloaded. Reload only after nginx -t.

Questions we keep getting

Do I still need TLS 1.2 on a public website?

TLSRef Intermediate still includes it. If a quarter of access logs shows no 1.2 handshake, move to Modern. Measure, then drop. Do not keep 1.0 or 1.1 in that measurement.

Is a 398-day certificate I already have invalid?

Certificates issued before 15 March 2026 could still be 398 days. New issuance is 200. Replace on your ACME loop. Do not wait for notAfter if the box cannot renew itself.

Does HSTS replace the 301?

No. The first visit, and any client that has no HSTS cache, still needs the redirect. HSTS stops the next visit from trying HTTP. Preload stops even the first visit, after the browser build that ships your name. Keep the 301.

Aphinya Dechalert

Aphinya Dechalert / About Author

Aphinya is a skilled technical writer with field experiences in software development, agile, and JavaScript full stack with AWS and Google cloud. She is a developer advocate and community builder, helping others navigate their journeys and careers as developers.