100% on SSL Labs with Nginx


Today concluded a long road for me. Not because the route “was not droite” but because “la pente was rude”. I managed to get a 100% score on SSL Labs. It is not that difficult, but because TLS security is an always evolving subject, it is quite difficult to find up-to-date information on how to do it. Plus I wanted to switch from lighttpd to Nginx before because I wanted http/2 support more than 100% on SSL Labs. But like always, life had the better of me and I was quite busy with more important things to do…

That and the fact it took quite a bit of time (about 45 minutes) for me to understand that TLS 1.3 on Nginx was responsible for the missing 10%.


I also identified that, in order to achieve 100%, I had to renew my certificate because I was using a prime256v1 curve and that automaticaly puts you at 90% for key exchange…

Understanding SSL Labs scores.

The provided document helps a lot to get close to 100%. You can find it at https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide However, you have to read quite a bit of bugs and issues reports to really pinpoints the cause of missing percents…

Going for the Elliptic Curve Certificat

Note: If you still want to use a RSA certificate, you MUST use a 4096bits private key to reach the 100% score.

First we generate the EC parameters.

openssl ecparam -name secp384r1 -out server.ecparam.pem

Then we use the ecparams to create the private key.

openssl ecparam -in server.ecparam.pem -genkey -noout -out server.key

Finaly we create the new certificate request.

openssl req -new -key server.key -out server.csr -sha256

TLS 1.3 is not ready yet

Sadly, there is a current bug in Nginx where it does not respect the cipher order for TLS 1.3 so if you want the sweet 100%, you will have to deactivate it (you can use a workaround, but I’d rather wait for the issue to be fixed by Nginx).

OCSP Stapling

For OCSP stapling to work, we need to supply the full certificate chain. You can download it from your certificate issuer. In my case it’s available at from Gandi’s TLS Certificate administration interface. We just need to append it to the server’s public certificate file.

cat certificateChain.pem >> server.pem

Nginx Configuration

Here is the abridged Nginx configuration.

server {
  listen [::]:443 default_server ssl http2;
  listen      443 default_server ssl http2;

  server_name opium.io www.opium.io;
  root /lighttpd-jail;

  # Certificate(s) and private key
  ssl_certificate /etc/nginx/tls/server.pem;
  ssl_certificate_key /etc/nginx/tls/server.key;
  ssl_ecdh_curve secp384r1;
  ssl_dhparam /etc/nginx/tls/dhparam.4096.pem;

  ssl_protocols TLSv1.2;
  ssl_prefer_server_ciphers on;

  ssl_session_cache shared:TLS:10m;
  ssl_session_timeout 10m;
  ssl_buffer_size 4k;

  # OCSP stapling
  ssl_stapling on;
  ssl_stapling_verify on;

  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;preload";
  add_header X-Frame-Options DENY;
  add_header X-Content-Type-Options nosniff;
  add_header X-XSS-Protection "1; mode=block";

  server_tokens off;
location / {
        try_files $uri $uri/ =404;