How to Achieve 100% SSL Labs Score: Optimizing Key Exchange with Perfect Forward Secrecy


2 views

When testing my server at ssllabs.com, I noticed the Key Exchange score wasn't reaching 100% despite using a strong 4096-bit Let's Encrypt certificate. My current Apache configuration looks solid:

SSLCipherSuite AES256+EECDH:AES256+EDH:!aNULL
SSLHonorCipherOrder on
SSLProtocol all -TLSv1.1 -TLSv1 -SSLv3 -SSLv2

Yet the results showed room for improvement, particularly in the ephemeral key exchange parameters.

After researching, I discovered that while my configuration enabled Perfect Forward Secrecy (PFS), the default DH parameters used by Apache might be suboptimal. The solution was to generate custom DH parameters:

openssl dhparam -out dhparam.pem 4096

This generates 4096-bit Diffie-Hellman parameters - a process that can take hours on some systems. After generating the file, I configured Apache to use it:

SSLOpenSSLConfCmd DHParameters "/etc/ssl/certs/dhparam.pem"

Even after implementing custom DH parameters, my score didn't improve. The missing element was proper configuration of elliptic curve Diffie-Hellman (ECDH) parameters. Adding this to my Apache config made the difference:

SSLOpenSSLConfCmd Curves secp384r1
SSLProtocol all -TLSv1.1 -TLSv1 -SSLv3 -SSLv2
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
SSLHonorCipherOrder on

Here's the complete configuration that achieved perfect scores across all categories:

# Certificate configuration
SSLCertificateFile /etc/letsencrypt/live/domain.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/domain.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/domain.com/chain.pem

# Protocol and cipher configuration
SSLProtocol all -TLSv1.1 -TLSv1 -SSLv3 -SSLv2
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder on

# DH and ECDH parameters
SSLOpenSSLConfCmd DHParameters "/etc/ssl/certs/dhparam.pem"
SSLOpenSSLConfCmd Curves secp384r1

# HSTS header
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

After implementing these changes and restarting Apache, the ssllabs.com test showed:

  • Certificate: 100%
  • Protocol Support: 100%
  • Key Exchange: 100%
  • Cipher Strength: 100%

The server now supports only TLS 1.2 and 1.3 with perfect forward secrecy using both traditional DH and elliptic curve cryptography.


When chasing that elusive 100% SSL Labs score, many administrators hit a wall at the Key Exchange section despite using strong 4096-bit certificates. The missing piece often lies in proper Diffie-Hellman parameter configuration.

Your current Apache setup shows good practices:

SSLCipherSuite AES256+EECDH:AES256+EDH:!aNULL
SSLHonorCipherOrder on
SSLProtocol all -TLSv1.1 -TLSv1 -SSLv3 -SSLv2

However, the key exchange score remains stubbornly below 100%. Let's examine why.

While generating 4096-bit DH parameters was technically correct:

openssl dhparam -out dhparam.pem 4096

The critical oversight is that modern TLS implementations prefer ECDHE over traditional DH. SSL Labs specifically rewards forward secrecy implementations using elliptic curves.

Here's the complete solution combining both traditional DH and modern ECDH:

# Cipher suite prioritization
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder on

# Protocol restrictions
SSLProtocol all -TLSv1.1 -TLSv1 -SSLv3 -SSLv2

# DH Parameters (still required for DHE cipher suites)
SSLOpenSSLConfCmd DHParameters "/etc/ssl/certs/dhparam.pem"

# ECDH curve configuration (critical for perfect score)
SSLOpenSSLConfCmd Curves secp384r1

The configuration achieves:

  • Perfect forward secrecy via both ECDHE and DHE
  • Strong 384-bit elliptic curve cryptography
  • Proper prioritization of GCM cipher modes
  • Backward compatibility while maximizing score

After implementing these changes:

  1. Restart Apache: systemctl restart apache2
  2. Test locally: openssl s_client -connect yourdomain:443 -cipher ECDHE
  3. Verify on SSL Labs after propagation

While this achieves 100%, be aware that:

  • secp384r1 has higher computational overhead than secp256r1
  • 4096-bit DH parameters impact handshake performance
  • Consider testing with ssl-speed if serving high traffic

If supporting only modern clients, this minimalist config also achieves 100%:

SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384
SSLProtocol TLSv1.2
SSLOpenSSLConfCmd Curves X25519:secp384r1