Wiki / TLS Hardening

How to fix weak cipher and deprecated TLS protocol advisories on Apache, Nginx, and IIS

Last updated

How to fix weak cipher and deprecated TLS protocol advisories on Apache, Nginx, and IIS

This is the most common advisory CertTracker fires, and one of the most consequential. A server that still negotiates TLS 1.0, TLS 1.1, RC4, 3DES, or anonymous-DH cipher suites is publicly broadcasting an attack surface that every reputable browser already refuses to use.

This article covers what triggers the advisory, why each issue matters, and exact configuration changes to make on the four most common stacks — Apache, Nginx, IIS, and HAProxy — followed by a verification step.

What triggers the advisory

CertTracker raises a weak_cipher or deprecated_protocol advisory when, on poll, the TLS handshake completes using any of:

Deprecated protocols

  • SSL 2.0, SSL 3.0
  • TLS 1.0, TLS 1.1

All four have been formally deprecated by the IETF. Apple, Google, Mozilla, and Microsoft removed support from their browsers between 2020 and 2021.

Weak ciphers

  • RC4 (any variant)
  • 3DES (DES-CBC3-SHA, etc.)
  • DES (single-DES)
  • EXPORT-grade ciphers (40- and 56-bit keys)
  • NULL ciphers (no encryption)
  • Anonymous Diffie-Hellman (ADH-*, AECDH-*)
  • DH key parameters smaller than 2048 bits

The advisory also fires when a handshake fails in a way that implies one of the above was offered — the OpenSSL error string isn't always unambiguous, so if rc4.badssl.com rejects you with SSLV3_ALERT_HANDSHAKE_FAILURE, CertTracker errs on the side of flagging it.

Why it matters

These configurations don't just lower your security grade. Real-world consequences:

  • Chrome, Firefox, Safari, and Edge all refuse to negotiate TLS below 1.2 and will display a hard error page to users — not a warning, an error.
  • PCI-DSS, HIPAA, and most other compliance frameworks explicitly require TLS 1.2 or higher.
  • Many corporate proxies and load balancers will reject the connection at L7 entirely.
  • Some search engines treat the endpoint as effectively offline when their crawlers can't complete a handshake.

Fix on Apache (httpd 2.4+)

Edit your ssl.conf or virtual-host config:

SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder     on
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLOpenSSLConfCmd       Curves X25519:secp521r1:secp384r1:prime256v1

SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 reads "all protocols except SSLv3, TLS 1.0, and TLS 1.1" — i.e. TLS 1.2 and TLS 1.3 only.

Reload Apache:

sudo apachectl configtest && sudo systemctl reload apache2   # Debian/Ubuntu
sudo apachectl configtest && sudo systemctl reload httpd     # RHEL/CentOS

Fix on Nginx (1.13+)

Edit your nginx.conf or per-server ssl.conf:

ssl_protocols             TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
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_ecdh_curve            X25519:secp521r1:secp384r1:prime256v1;

Reload Nginx:

sudo nginx -t && sudo systemctl reload nginx

Fix on IIS (Windows Server 2016+)

IIS doesn't expose TLS protocol selection in the GUI — it's a registry-level setting under HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols.

The reliable way to set this is the open-source IISCrypto tool from Nartac Software, or PowerShell. With PowerShell, run as administrator:

# Disable deprecated protocols
foreach ($proto in 'SSL 2.0', 'SSL 3.0', 'TLS 1.0', 'TLS 1.1') {
    $base = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$proto"
    foreach ($role in 'Server', 'Client') {
        $key = "$base\$role"
        New-Item -Path $key -Force | Out-Null
        New-ItemProperty -Path $key -Name 'Enabled' -Value 0 -PropertyType DWord -Force | Out-Null
        New-ItemProperty -Path $key -Name 'DisabledByDefault' -Value 1 -PropertyType DWord -Force | Out-Null
    }
}

# Enable TLS 1.2 explicitly
$tls12 = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2"
foreach ($role in 'Server', 'Client') {
    $key = "$tls12\$role"
    New-Item -Path $key -Force | Out-Null
    New-ItemProperty -Path $key -Name 'Enabled' -Value 1 -PropertyType DWord -Force | Out-Null
    New-ItemProperty -Path $key -Name 'DisabledByDefault' -Value 0 -PropertyType DWord -Force | Out-Null
}

A reboot is required for SCHANNEL protocol changes to take effect — there is no way around this on Windows. Plan the change accordingly.

For cipher suite ordering, use the Group Policy editor: Computer Configuration → Administrative Templates → Network → SSL Configuration Settings → SSL Cipher Suite Order, set the order to a Mozilla intermediate or modern list. IISCrypto's "Best Practices" template handles both protocols and cipher ordering in one click and is what most Windows administrators use in practice.

Fix on HAProxy (2.0+)

In haproxy.cfg:

global
    ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
    ssl-default-bind-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-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256

Reload HAProxy:

sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy

Cloud and managed-service notes

  • AWS ELB / ALB / CloudFront — change the security policy on the listener to ELBSecurityPolicy-TLS13-1-2-2021-06 (ALB/NLB) or TLSv1.2_2021 (CloudFront). One setting, no protocol/cipher list to maintain.
  • Azure App Service / Application Gateway — set the Minimum TLS version to 1.2 in the TLS/SSL settings blade. Application Gateway also has a SSL Policy dropdown — pick AppGwSslPolicy20220101 or newer.
  • Cloudflare — Dashboard → SSL/TLS → Edge Certificates → set Minimum TLS Version to TLS 1.2. Cloudflare manages cipher selection automatically.
  • F5 BIG-IP — modify the Client SSL profile to exclude TLS 1.0/1.1 in the Disabled Protocols list, and assign a modern cipher group like ECDHE+AES-GCM:CHACHA20.

Verify the fix

After the change, do all three of these checks before clearing the advisory:

1. Confirm with openssl from any client:

# Should FAIL (server should reject TLS 1.0):
openssl s_client -connect your-host.example.com:443 -tls1 < /dev/null

# Should SUCCEED:
openssl s_client -connect your-host.example.com:443 -tls1_2 < /dev/null

2. Run a Qualys SSL Labs test:

Visit https://www.ssllabs.com/ssltest/analyze.html?d=your-host.example.com&hideResults=on and confirm:

  • Grade is A or A+
  • Protocols section shows only TLS 1.2 and TLS 1.3 as Yes; older as No
  • Cipher suites section shows no entries flagged as WEAK

3. Trigger a re-scan from the CertTracker UI. The fastest path: open the monitor's detail modal (click the monitor row on the Monitors page), scroll to the Active Advisories section, and click the orange ⟳ Re-scan button on the right side of the section header. The status line below the header will confirm the cert re-poll and TLS audit are queued; the next 5-minute poller tick (within ~5 minutes) runs both, and the advisory clears if your fix took. See Where to find these buttons in the UI for visual landmarks.

Open the monitor in CertTracker, click Force Poll in the detail modal, wait for the next poll to complete (typically under a minute), and confirm the Active Advisories section no longer lists weak cipher / deprecated protocol. Then click Acknowledge on the now-resolved advisory if it's still showing — that closes the alert thread.

Common pitfalls

  • Multiple listeners on the same host — if your web server is also bound to port 8443 or runs an admin console on a non-standard port, fix all of them. CertTracker polls per (hostname, port) combo, so the advisory will keep firing for the listener you missed.
  • Reverse proxy in front — if Nginx fronts an Apache app server, the relevant config is on Nginx, not Apache. The TLS handshake terminates at whichever process is bound to 443 on the public IP. Fix that one.
  • Load balancer SSL termination — if a load balancer terminates TLS, the LB config controls what protocols/ciphers are negotiated with the client. Fixing the backend app server has no effect on what CertTracker sees.
  • CDN caching — if your edge is on a CDN, the CDN edge config is what matters. Origin changes are invisible to external observers.

Related advisories

  • Modern clients cannot connect — fires when TLS 1.2/1.3 are also disabled, leaving nothing for modern browsers to negotiate. The fix is the same as above but more urgent.
  • Excessive validity period — sometimes shows up together with this advisory when an old internal cert profile is in play.

Back to the TLS hardening hub.