TLS configuration errors remain common in production web infrastructure. Some are the result of stale default configurations that were set years ago and never revisited. Others come from copy-paste configuration examples that are outdated. Whatever the cause, the effect is the same: users connect to a site they believe is secure using a configuration that is not.
This article covers the TLS issues most frequently found during WebDefect audits, explains the concrete risk each one poses, and provides remediation steps for the most common web server configurations.
Deprecated TLS Versions Still Enabled
TLS 1.0 and TLS 1.1 are deprecated. The IETF formally deprecated both in RFC 8996 (published March 2021). All major browser vendors removed support for TLS 1.0 and TLS 1.1 in 2020. The deprecation reflects known weaknesses in the protocols, including vulnerability to BEAST (TLS 1.0), POODLE (SSL 3.0 but related attack class), and SLOTH attacks, as well as the use of MD5 and SHA-1 in the older protocol handshake.
Despite this, many servers still have TLS 1.0 or TLS 1.1 enabled in their configuration. These servers will not negotiate TLS 1.0 or 1.1 with modern browsers (because the browsers no longer offer those versions), but the configuration represents a risk if a non-browser client or an attacker-controlled client attempts to negotiate a connection using an older version.
The correct configuration is to enable TLS 1.2 and TLS 1.3 only. TLS 1.3 provides forward secrecy by default and removes the negotiation of most cipher suite choices from the configuration, which reduces the risk of misconfiguration.
# nginx - disable TLS 1.0 and 1.1
ssl_protocols TLSv1.2 TLSv1.3;
# Apache
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1Weak Cipher Suites
Even with TLS 1.2 or 1.3, cipher suite selection matters. TLS 1.2 supports a wide range of cipher suites, including ones with known weaknesses. Suites to disable include:
- RC4-based suites: RC4 is cryptographically broken.
- NULL encryption suites: These authenticate the connection but transmit data in plaintext.
- Export suites (EXPORT): Intentionally weakened key sizes, remnants of US export regulations.
- DES and 3DES suites: SWEET32 attack exploits 3DES birthday bounds; DES key sizes are below modern standards.
- Suites without forward secrecy (RSA key exchange): These use the server's RSA key directly for key exchange. If the private key is ever compromised, past sessions can be decrypted.
A reasonable TLS 1.2 cipher suite configuration for nginx:
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;TLS 1.3 cipher suites are fixed by the specification and cannot be misconfigured in the same way. All TLS 1.3 suites provide forward secrecy and use AEAD encryption.
Certificate Issues
Expired Certificates
An expired certificate causes browser warnings for all visitors. Most browsers block the connection entirely with a full-page warning that users must explicitly bypass. The impact is immediate and visible.
Expired certificates are preventable with monitoring. Certificate expiry monitoring should be treated as a basic operational requirement. Set alerts at 30 days and 7 days before expiry. Many certificate authorities send email reminders, but these should be treated as a backup rather than a primary monitoring mechanism.
Tools like Let's Encrypt with certbot can automate certificate renewal entirely, eliminating expiry risk for domains where automated renewal is feasible. For certificates issued by commercial CAs, check that renewal workflows are documented and tested, not just theoretically in place.
Incomplete Certificate Chains
A complete certificate chain consists of the server's end-entity certificate, any intermediate CA certificates, and the root CA certificate (though browsers typically include roots in their own trust store and do not require the server to send it). When intermediate certificates are missing, some clients, particularly non-browser HTTP clients and older mobile clients, cannot build the chain to a trusted root and will reject the connection.
Browser clients often fetch missing intermediate certificates automatically via the Authority Information Access (AIA) extension in the certificate. This masks the configuration issue in typical testing but does not fix it. Clients that do not support AIA chasing (many server-to-server API clients) will fail.
To verify your chain is complete:
openssl s_client -connect example.com:443 -showcerts 2>/dev/null | openssl x509 -noout -text | grep -A2 "Authority Information Access"The Mozilla SSL Configuration Generator includes intermediate certificates in the generated configuration for all supported server types.
Self-Signed Certificates on Production
Self-signed certificates are appropriate for development and internal tools. Using one on a public-facing production domain means every visitor receives an untrusted certificate warning and must choose to bypass it. This trains users to ignore certificate warnings, which undermines the security value of certificate validation entirely. The practical risk is that users will habitually bypass warnings on other sites where the warning indicates genuine MitM risk.
Mixed Content
A page served over HTTPS that loads sub-resources (scripts, images, stylesheets, frames) over HTTP is serving mixed content. Modern browsers block active mixed content (scripts, iframes) by default and display warnings for passive mixed content (images). The security concern is that a sub-resource loaded over HTTP can be modified by a network attacker, even though the parent page is served over HTTPS.
The fix is to update all sub-resource URLs to use HTTPS. The Content-Security-Policy: upgrade-insecure-requests directive instructs the browser to automatically upgrade HTTP sub-resource requests to HTTPS, which can serve as a temporary mitigation while URLs are updated in code.
Missing HTTP to HTTPS Redirect
If a server accepts connections on port 80 but does not redirect to HTTPS, users who connect over HTTP receive the site content unencrypted. The correct configuration is a 301 (permanent) redirect from all HTTP URLs to the corresponding HTTPS URL.
# nginx: redirect all HTTP to HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}Using 301 (permanent) rather than 302 (temporary) is correct for production. 302 redirects do not contribute to HSTS preloading eligibility and may be cached differently by intermediaries.
Remediation Guidance
The Mozilla SSL Configuration Generator (ssl-config.mozilla.org) produces server-specific TLS configurations for nginx, Apache, HAProxy, and other servers that disable deprecated versions and weak ciphers automatically. Using a generated configuration as a starting point is preferable to hand-editing cipher lists.
For certificate management, implement automated renewal where feasible. Document renewal procedures and expiry monitoring for manually managed certificates. Test renewal before it is urgently needed.
Verification
After making TLS configuration changes, verify the result with a dedicated TLS scanner. The SSL Labs server test (ssllabs.com/ssltest) provides detailed output covering supported protocol versions, cipher suites, certificate chain status, and known vulnerabilities. It is a useful verification tool but is publicly visible (results are logged) and should not be used on internal or sensitive infrastructure.
For command-line verification:
# Check supported TLS versions
openssl s_client -connect example.com:443 -tls1 2>&1 | grep "Protocol"
openssl s_client -connect example.com:443 -tls1_1 2>&1 | grep "Protocol"
openssl s_client -connect example.com:443 -tls1_2 2>&1 | grep "Protocol"
openssl s_client -connect example.com:443 -tls1_3 2>&1 | grep "Protocol"
# Check certificate expiry
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates