Antoine Duno
Founder of ZeriFlow · 10 years fullstack engineering · About the author
Key Takeaways
- TLS is the foundation of web security — but most developers configure it once and never look at it again. This guide covers the practical details: TLS 1.2 vs 1.3 differences, how to choose cipher suites, certificate validation mechanics, OCSP stapling, and the most common TLS misconfigurations that still show up in production.
- Includes copy-paste code examples and step-by-step instructions.
- Free automated scan available to verify your implementation.
TLS/SSL for Developers: A Practical Security Guide (2026)
TLS (Transport Layer Security) is the protocol that encrypts the connection between a browser and your server. Without it, every HTTP request travels across the network in plain text — readable by any device on the path between the client and server.
Most developers know they need HTTPS. Fewer understand the details that separate a properly configured TLS setup from a merely functional one. This guide covers the parts that matter: protocol versions, cipher suites, certificate validation, OCSP stapling, and the misconfigurations that frequently show up in SSL certificate checks.
A Brief History: SSL to TLS
You will often hear SSL and TLS used interchangeably, but SSL (Secure Sockets Layer) is technically obsolete. The versions you need to know:
| Version | Status | Notes |
|---|---|---|
| SSL 2.0 | Deprecated 1996 | Multiple critical flaws |
| SSL 3.0 | Deprecated 2015 | POODLE attack |
| TLS 1.0 | Deprecated 2020 | BEAST, POODLE-TLS vulnerabilities |
| TLS 1.1 | Deprecated 2021 | Weak cipher suites |
| TLS 1.2 | Current | Still widely used; cipher suite selection matters |
| TLS 1.3 | Current | Recommended; faster and more secure by design |
In 2026, you should be running TLS 1.3 as your primary protocol, with TLS 1.2 as an optional fallback. Anything older should be explicitly disabled.
TLS 1.2 vs TLS 1.3: Key Differences
Performance
TLS 1.3 reduced the handshake from 2 round trips (TLS 1.2) to 1 round trip. This is significant for latency — especially for users far from your servers. TLS 1.3 also supports 0-RTT resumption for returning connections, allowing data to be sent on the first message of a resumed session.
Security
TLS 1.3 removed all cipher suites considered weak: - No RSA key exchange (vulnerable to future-decryption: recorded traffic could be decrypted if the private key is later compromised) - No CBC mode cipher suites (BEAST, Lucky Thirteen attacks) - No RC4, DES, 3DES, MD5, SHA-1 in the protocol - All TLS 1.3 cipher suites provide Forward Secrecy (each connection uses an ephemeral key)
TLS 1.2 is still secure when configured correctly, but requires explicit removal of these weak options.
Simplified Cipher Suite Selection
In TLS 1.3, the five supported cipher suites are all secure and there is nothing to configure — the protocol enforces them. In TLS 1.2, you need to explicitly choose which suites to enable.
Cipher Suites
A cipher suite is a combination of algorithms used for key exchange, authentication, encryption, and message authentication. In TLS 1.2 notation:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
│ │ │ │ │
│ │ │ │ └── MAC algorithm (HMAC-SHA384)
│ │ │ └── Encryption (AES-256-GCM)
│ │ └── Authentication (RSA certificate)
│ └── Key Exchange (ECDHE — provides forward secrecy)
└── ProtocolRecommended TLS 1.2 Cipher Suites (2026)
# Nginx — TLS 1.2 cipher suites (in order of preference)
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;The key rules:
- Only ECDHE or DHE key exchange (provides forward secrecy — no static RSA)
- Only GCM or ChaCha20-Poly1305 for encryption (AEAD modes — no CBC)
- Only SHA-256 or SHA-384 (no SHA-1)
- ssl_prefer_server_ciphers off — let the client choose from the allowed list to get the best cipher the client supports
TLS 1.3 Cipher Suites (Automatic)
TLS 1.3 supports exactly these cipher suites, all of which are strong:
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
There is nothing to configure — OpenSSL handles this automatically.
Server Configuration Examples
Nginx (Recommended 2026 Configuration)
server {
listen 443 ssl http2;
server_name yourdomain.com;
# Certificate and key
ssl_certificate /etc/ssl/certs/yourdomain.com.crt;
ssl_certificate_key /etc/ssl/private/yourdomain.com.key;
# Protocol versions — TLS 1.2 and 1.3 only
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher suites for TLS 1.2 (TLS 1.3 is automatic)
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;
# DH parameters (for TLS 1.2 DHE key exchange)
ssl_dhparam /etc/nginx/dhparam.pem;
# Session resumption
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # ~40,000 sessions
ssl_session_tickets off;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# ... rest of your config
}Generate DH parameters (do this once):
openssl dhparam -out /etc/nginx/dhparam.pem 2048Apache
SSLEngine on
SSLCertificateFile /etc/ssl/certs/yourdomain.com.crt
SSLCertificateKeyFile /etc/ssl/private/yourdomain.com.key
SSLCertificateChainFile /etc/ssl/certs/chain.pem
# Protocols
SSLProtocol -all +TLSv1.2 +TLSv1.3
# Cipher suites
SSLCipherSuite TLSv1.3 TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
SSLCipherSuite TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
# OCSP stapling
SSLUseStapling on
SSLStaplingCache shmcb:/var/run/ocsp(128000)Certificate Validation
What Browsers Verify
When a browser connects to your server, it validates the certificate chain:
- 1Signature chain: The certificate must be signed by an intermediate CA, which is signed by a trusted root CA in the browser''s trust store
- 2Expiry: The certificate''s
notAfterdate must be in the future - 3Domain match: The certificate''s Subject Alternative Names (SAN) must include the domain being accessed
- 4Revocation: The certificate must not have been revoked (checked via OCSP or CRL)
Certificate Types
| Type | Domain Coverage | Validation Level | Common Use |
|---|---|---|---|
| Single domain | One domain | DV, OV, or EV | Single site |
Wildcard (*.domain.com) | All direct subdomains | DV or OV | Multiple subdomains |
| Multi-domain (SAN) | Multiple specific domains | DV, OV, or EV | Multiple sites |
Domain Validation (DV): Only proves you control the domain. Issued in minutes. Let''s Encrypt issues DV certificates.
Organisation Validation (OV): Verifies the organisation''s identity. Takes days. Shows company name in certificate details.
Extended Validation (EV): Strictest vetting. The green bar/company name that used to appear in browsers. Most browsers no longer display EV indicators prominently, reducing the UX distinction.
For most applications, DV certificates from Let''s Encrypt are entirely appropriate.
Certificate Chain Issues
One of the most common TLS configuration errors is an incomplete certificate chain. Your server must send: 1. Your domain certificate 2. Any intermediate certificates
If you omit the intermediates, some clients (especially older mobile browsers) will fail to connect because they do not have the intermediate cached.
Test your chain:
openssl s_client -connect yourdomain.com:443 -showcerts 2>/dev/null | grep "s:\\|i:"You should see your domain cert and the intermediate cert(s), but not the root (the root is in the browser''s trust store).
OCSP Stapling
What OCSP Is
OCSP (Online Certificate Status Protocol) is the mechanism browsers use to check if a certificate has been revoked. Normally, this works like this:
- 1Browser receives your certificate
- 2Browser contacts the CA''s OCSP server to ask "is this cert valid?"
- 3CA''s OCSP server responds
- 4Browser proceeds (or not)
This has two problems: it adds latency to every new connection, and it reveals which sites your users are visiting to the CA''s OCSP server.
How OCSP Stapling Fixes This
With OCSP stapling, your server fetches the OCSP response from the CA, caches it, and attaches ("staples") it to the TLS handshake. The browser gets the revocation status directly from your server without a separate round-trip.
This is better for performance (no extra network request) and privacy (the CA''s OCSP server sees your server polling periodically rather than seeing every user connection).
OCSP responses are signed by the CA and time-limited (typically 24–48 hours), so they cannot be forged.
Enable it in Nginx as shown in the configuration above:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/chain.pem;
resolver 8.8.8.8;Verify it is working:
openssl s_client -connect yourdomain.com:443 -status 2>/dev/null | grep "OCSP Response Status"
# Should output: OCSP Response Status: successful (0x0)How to Run an SSL Certificate Check
Command Line
Full TLS details with OpenSSL:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -text -noout | grep -E "Subject:|DNS:|Not After|Issuer:"Check expiry date:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -datesCheck which TLS versions are supported:
# Test TLS 1.3
openssl s_client -connect yourdomain.com:443 -tls1_3 2>&1 | grep "Protocol"
# Test TLS 1.2
openssl s_client -connect yourdomain.com:443 -tls1_2 2>&1 | grep "Protocol"
# Test TLS 1.1 (should fail on a properly configured server)
openssl s_client -connect yourdomain.com:443 -tls1_1 2>&1 | grep -E "Protocol|alert"Check cipher suites with nmap:
nmap --script ssl-enum-ciphers -p 443 yourdomain.comSSL Labs
Qualys SSL Labs (ssllabs.com) provides the industry-standard TLS configuration grade (A+, A, B, C, D, F). An A+ requires: - No support for TLS 1.0 or 1.1 - No weak cipher suites - HSTS configured - No known vulnerabilities (BEAST, POODLE, ROBOT, etc.)
Automated Scan
ZeriFlow includes TLS checking as part of its full 80+ check security scan — it validates your protocol versions, certificate expiry, certificate chain, OCSP stapling, and HSTS configuration in the same pass as all your other security checks.
Common TLS Misconfigurations
1. TLS 1.0 or 1.1 Still Enabled
Many default server configurations enable TLS 1.0 for "compatibility." In 2026, no legitimate client requires TLS 1.0. Disable it.
2. Expired Certificate
The most embarrassing TLS failure. Automate certificate renewal:
# Certbot auto-renewal (runs twice daily)
systemctl enable certbot.timer
systemctl start certbot.timer
# Verify
certbot renew --dry-run3. Self-Signed Certificate in Production
Self-signed certificates cause browser trust errors. Use Let''s Encrypt — it is free, automated, and trusted by all major browsers.
4. Missing Intermediate Certificate
Browsers fail to verify the chain. Include intermediates in your certificate file:
cat yourdomain.com.crt intermediate.crt > yourdomain.com-chain.crt5. Certificate / Domain Mismatch
Serving api.yourdomain.com with a certificate for yourdomain.com only. Use a wildcard (*.yourdomain.com) or multi-domain (SAN) certificate.
6. SSL Session Tickets Without Key Rotation
Session tickets allow session resumption but use a key that must be rotated regularly. If the session ticket key is compromised, historical session recordings can be decrypted (breaking forward secrecy). Disable session tickets unless you implement proper key rotation:
ssl_session_tickets off;7. Weak DH Parameters
If you use DHE cipher suites with TLS 1.2, the DH parameter size matters. Use at least 2048 bits:
openssl dhparam -out /etc/nginx/dhparam.pem 2048Let''s Encrypt and Certificate Automation
Let''s Encrypt issues free, 90-day DV certificates with automated renewal. The 90-day lifespan encourages automation — if you are not automating renewal, you will get expired certificates.
# Install Certbot (Ubuntu/Debian)
apt install certbot python3-certbot-nginx
# Obtain and install certificate
certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Test auto-renewal
certbot renew --dry-runFor containerised environments, consider using Traefik or Caddy, which handle Let''s Encrypt certificate issuance and renewal automatically with zero configuration.
Summary
A properly configured TLS setup in 2026 requires:
- TLS 1.3 enabled; TLS 1.2 with modern cipher suites only; TLS 1.0/1.1 disabled
- Valid certificate from a trusted CA with the full chain included
- OCSP stapling enabled for performance and privacy
- Certificate automation (Let''s Encrypt + Certbot) to prevent expiry
- HSTS configured to lock in HTTPS for all visitors
- Session tickets disabled or rotated regularly
Run a comprehensive SSL certificate check with ZeriFlow to verify your TLS configuration alongside all other security layers.